- 剑指 Offer 03. 数组中重复的数字">剑指 Offer 03. 数组中重复的数字
- ">

- 剑指 Offer 04. 二维数组中的查找">剑指 Offer 04. 二维数组中的查找
- 剑指 Offer 11. 旋转数组的最小数字">剑指 Offer 11. 旋转数组的最小数字
- 剑指 Offer 17. 打印从1到最大的n位数">剑指 Offer 17. 打印从1到最大的n位数
- 剑指 Offer 21. 调整数组顺序使奇数位于偶数前面">剑指 Offer 21. 调整数组顺序使奇数位于偶数前面
- 剑指 Offer 29. 顺时针打印矩阵">剑指 Offer 29. 顺时针打印矩阵
- 剑指 Offer 39. 数组中出现次数超过一半的数字">剑指 Offer 39. 数组中出现次数超过一半的数字
- 剑指 Offer 40. 最小的k个数">剑指 Offer 40. 最小的k个数
- 剑指 Offer 42. 连续子数组的最大和">剑指 Offer 42. 连续子数组的最大和
- 剑指 Offer 47. 礼物的最大价值">剑指 Offer 47. 礼物的最大价值
- 剑指 Offer 51. 数组中的逆序对">剑指 Offer 51. 数组中的逆序对
- 剑指 Offer 53 - I. 在排序数组中查找数字 I">剑指 Offer 53 - I. 在排序数组中查找数字 I
- 剑指 Offer 53 - II. 0~n-1中缺失的数字">剑指 Offer 53 - II. 0~n-1中缺失的数字
- 剑指 Offer 56 - I. 数组中数字出现的次数">剑指 Offer 56 - I. 数组中数字出现的次数
- 剑指 Offer 56 - II. 数组中数字出现的次数 II">剑指 Offer 56 - II. 数组中数字出现的次数 II
- 剑指 Offer 57. 和为s的两个数字">剑指 Offer 57. 和为s的两个数字
- 剑指 Offer 61. 扑克牌中的顺子">剑指 Offer 61. 扑克牌中的顺子
- 1. 两数之和">1. 两数之和
- 15. 三数之和">15. 三数之和
- 53. 最大子序和">53. 最大子序和
- 88. 合并两个有序数组">88. 合并两个有序数组
- 136. 只出现一次的数字">136. 只出现一次的数字
- 485. 最大连续 1 的个数">485. 最大连续 1 的个数
- 9. 回文数">9. 回文数
剑指 Offer 03. 数组中重复的数字
找出数组中重复的数字。





class Solution {public int findRepeatNumber(int[] nums) {Set<Integer> set = new HashSet<>();int repeat = -1;for(int num : nums){if(!set.add(num)){repeat = num;break;}}return repeat;}}class Solution {public int findRepeatNumber(int[] nums) {int i = 0;while(i < nums.length) {if(nums[i] == i) {i++;continue;}if(nums[nums[i]] == nums[i]) return nums[i];int tmp = nums[i];nums[i] = nums[tmp];nums[tmp] = tmp;}return -1;}}
class Solution {public int findRepeatNumber(int[] nums) {int res = 0;HashMap<Integer, Integer> hashMap = new HashMap<>();for(int i = 0; i < nums.length; i++){if(hashMap.containsKey(nums[i])){res = nums[i];break;}else{hashMap.put(nums[i], 1);}}return res;}}
剑指 Offer 04. 二维数组中的查找
在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个高效的函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

class Solution {public boolean findNumberIn2DArray(int[][] matrix, int target) {if(matrix == null || matrix.length == 0 || matrix[0].length == 0){return false;}int rows = matrix.length; //行数int clos = matrix[0].length; //列数//左上角起始坐标int row = 0;int clo = clos - 1;while(row < rows && clo >= 0){ //保证不越界if(matrix[row][clo] == target){return true;}else if(matrix[row][clo] > target){clo--;}else{row++;}}return false;}}
剑指 Offer 11. 旋转数组的最小数字
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。



class Solution {public int minArray(int[] numbers) {int low = 0;int height = numbers.length - 1;while(low < height){int pivot = low + (height - low) / 2;if(numbers[pivot] < numbers[height]){height = pivot;}else if(numbers[pivot] > numbers[height]){low = pivot + 1;}else if(numbers[pivot] == numbers[height]){height--;}}return numbers[low];}}
剑指 Offer 17. 打印从1到最大的n位数
输入数字
n,按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3,则打印出 1、2、3 一直到最大的 3 位数 999。

class Solution {public int[] printNumbers(int n) {if(n <= 0){return new int[0];}int max = (int)Math.pow(10, n);int[] arr = new int[max - 1];for(int i = 0; i < max - 1; i++){arr[i] = i + 1;}return arr;}}
剑指 Offer 21. 调整数组顺序使奇数位于偶数前面
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分。

class Solution {public int[] exchange(int[] nums) {if(nums == null || nums.length == 0) return nums;int[] res = new int[nums.length];int left = 0;int right = nums.length - 1;for(int i = 0; i < nums.length; i++){if(nums[i] % 2 != 0){res[left++] = nums[i]; //奇数从前往后放}else{res[right--] = nums[i]; //偶数从后往前放}}return res;}}
剑指 Offer 29. 顺时针打印矩阵
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。


class Solution {public int[] spiralOrder(int[][] matrix) {if(matrix.length == 0) return new int[0];int l = 0; //左边界int r = matrix[0].length - 1; //右边界int t = 0; //上边界int b = matrix.length - 1; //下边界int x = 0;int[] res = new int[(r + 1) * (b + 1)];while(true){for(int i = l; i <= r; i++) res[x++] = matrix[t][i]; //left to rightif(++t > b) break;for(int i = t; i <= b; i++) res[x++] = matrix[i][r]; //top to bottomif(l > --r) break;for(int i = r; i >= l; i--) res[x++] = matrix[b][i]; //right to leftif(t > --b) break;for(int i = b; i >= t; i--) res[x++] = matrix[i][l]; //bottom to topif(++l > r) break;}return res;}}
剑指 Offer 39. 数组中出现次数超过一半的数字
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。




//方法3: 摩尔投票法class Solution {public int majorityElement(int[] nums) {int res = 0, rating = 0;for(int num : nums){if(rating == 0) res = num;if(num == res){rating += 1;}else{rating -= 1;}}return res;}}//方法1:哈希表class Solution {public int majorityElement(int[] nums) {Map<Integer, Integer> map = new HashMap<>();for(int i = 0; i < nums.length; i++){int count = map.getOrDefault(nums[i], 0 ) + 1;if(count > nums.length / 2) return nums[i];map.put(nums[i], count);}return -1;}}//方法2:库函数排序class Solution {public int majorityElement(int[] nums) {Arrays.sort(nums);return nums[nums.length/2];}}
剑指 Offer 40. 最小的k个数
输入整数数组
arr,找出其中最小的k个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。


//利用快排算法class Solution {public int[] getLeastNumbers(int[] arr, int k) {if(k == 0 || arr.length == 0){return new int[0];}return qsk(arr, 0, arr.length - 1, k - 1);}private int[] qsk(int[] nums, int l, int r, int k){int j = quicksort(nums, l , r);if(j == k){return Arrays.copyOf(nums, j+1);}return j > k ? qsk(nums, l , j - 1, k): qsk(nums, j+1, r, k);}private int quicksort(int[] nums, int l ,int r){int v = nums[l];int i = l, j = r + 1;while(true){while(++i <= r && nums[i] < v);while(--j >=l && nums[j] > v);if(i >= j){break;}int temp = nums[i];nums[i] = nums[j];nums[j] = temp;}nums[l] = nums[j];nums[j] = v;return j; //保证Nums[j] 左边是最小的j个数字}}class Solution {public int[] getLeastNumbers(int[] arr, int k) {int[] res = new int[k];Queue<Integer> queue = new PriorityQueue<>(new Comparator<Integer>(){@Overridepublic int compare(Integer o1, Integer o2){return o2 - o1; //从小到大}});//Queue<Integer> queue = new PriorityQueue<>((o1, o2) -> (o2 - o1));//大顶堆中保留最小的k个数for(int i = 0; i < arr.length; i++){queue.add(arr[i]);if(queue.size() > k) queue.poll();}for(int i = 0; i < k; i++){res[i] = queue.poll();}return res;}}
剑指 Offer 42. 连续子数组的最大和

class Solution {public int maxSubArray(int[] nums) {int res = nums[0];for(int i = 1; i < nums.length; i++){nums[i] += Math.max(nums[i - 1], 0);res = Math.max(res, nums[i]);}return res;}}class Solution{public int maxSubArray(int[] nums){int length = nums.length;int[] dp = new int[length];dp[0] = nums[0];int res = nums[0];for(int i = 1; i < length; i++){dp[i] = Math.max(dp[i - 1] + nums[i], nums[i]);res = Math.max(res, dp[i]);}return res;}}
剑指 Offer 47. 礼物的最大价值
在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物?




class Solution {public int maxValue(int[][] grid) {int row = grid.length, col = grid[0].length;int[] dp = new int[col+1];for(int i = 0; i < row; i++){for(int j = 1; j < col + 1; j++){dp[j] = Math.max(dp[j-1], dp[j]) + grid[i][j-1];}}return dp[col];}}class Solution {public int maxValue(int[][] grid) {//最经典二维动态规划//f[i][j] = max(f[i-1][j], f[i][j - 1])int m = grid.length, n = grid[0].length;for(int i = 0; i < m; i++){for(int j = 0; j < n; j++){if(i == 0 && j == 0) continue;if( i == 0){grid[i][j] += grid[i][j - 1];}else if(j == 0){grid[i][j] += grid[i - 1][j];}else{grid[i][j] += Math.max(grid[i - 1][j], grid[i][j - 1]);}}}return grid[m - 1][n - 1];}}class Solution {public int maxValue(int[][] grid) {int m = grid.length, n = grid[0].length;for(int j = 1; j < n; j++) // 初始化第一行grid[0][j] += grid[0][j - 1];for(int i = 1; i < m; i++) // 初始化第一列grid[i][0] += grid[i - 1][0];for(int i = 1; i < m; i++)for(int j = 1; j < n; j++)grid[i][j] += Math.max(grid[i][j - 1], grid[i - 1][j]);return grid[m - 1][n - 1];}}
剑指 Offer 51. 数组中的逆序对
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。



class Solution {int[] nums, tmp;public int reversePairs(int[] nums) {this.nums = nums;tmp = new int[nums.length];return mergeSort(0, nums.length - 1);}private int mergeSort(int l, int r) {// 终止条件if (l >= r) return 0;// 递归划分int m = (l + r) / 2;int res = mergeSort(l, m) + mergeSort(m + 1, r);// 合并阶段int i = l, j = m + 1;for (int k = l; k <= r; k++)tmp[k] = nums[k];for (int k = l; k <= r; k++) {if (i == m + 1)nums[k] = tmp[j++];else if (j == r + 1 || tmp[i] <= tmp[j])nums[k] = tmp[i++];else {nums[k] = tmp[j++];res += m - i + 1; // 统计逆序对}}return res;}}
剑指 Offer 53 - I. 在排序数组中查找数字 I
统计一个数字在排序数组中出现的次数。




class Solution {public int search(int[] nums, int target) {if(nums == null || nums.length == 0) return 0;int start = binarySearch(nums, target);int end = binarySearch(nums, target + 1);return end - start + (nums[end] == target ? 1 : 0);}private int binarySearch(int[] nums, int tar){int l = 0;int r = nums.length - 1;while(l < r){int mid = l + (r - l) / 2;if(nums[mid] < tar){l = mid + 1;}else{r = mid;}}return l;}}
剑指 Offer 53 - II. 0~n-1中缺失的数字
一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字
剑指 Offer 56 - I. 数组中数字出现的次数
一个整型数组
nums里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。

class Solution {public int[] singleNumbers(int[] nums) {//1.数组中只出现一次的两个数字的异或结果int sum = 0;for(int x : nums){sum ^= x;}//2.找到异或二进制中1所在的位置int index = 0;while((sum >> index & 1) == 0){index++;}int first = 0;for(int x : nums){if((x >> index & 1) == 0){first ^= x;}}int second = sum ^ first;return new int[]{first, second};}}
剑指 Offer 56 - II. 数组中数字出现的次数 II
在一个数组
nums中除一个数字只出现一次之外,其他数字都出现了三次。请找出那个只出现一次的数字。


class Solution {public int singleNumber(int[] nums) {int[] count = new int[32];for(int num : nums){for(int i = 0; i < 32; i++){count[i] += num & 1;num >>= 1;}}int res = 0, mod = 3;for(int i = 0; i< 32; i++){res <<= 1;res += count[31 - i] % mod;}return res;}}
剑指 Offer 57. 和为s的两个数字
输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,则输出任意一对即可。


class Solution {public int[] twoSum(int[] nums, int target) {int i = 0;int j = nums.length - 1;while(i < j){int s = nums[i] + nums[j];if(s < target){i++;}else if(s > target){j--;}else{return new int[]{nums[i],nums[j]};}}return null;}}class Solution{public int[] twoSum(int[] nums, int target){Set<Integer> s = new HashSet<>();for(int i = 0; i < nums.length; i++){s.add(nums[i]);}for(int i = 0; i < nums.length; i++){if(s.contains(target - nums[i])){return new int[]{nums[i], target - nums[i]};}}return null;}}
剑指 Offer 61. 扑克牌中的顺子
从扑克牌中随机抽5张牌,判断是不是一个顺子,即这5张牌是不是连续的。2~10为数字本身,A为1,J为11,Q为12,K为13,而大、小王为 0 ,可以看成任意数字。A 不能视为 14。
1. 两数之和
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标 你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。 你可以按任意顺序返回答案。


class Solution {public int[] twoSum(int[] nums, int target) {Map <Integer,Integer> hashMap = new HashMap<>();for(int i=0;i<nums.length;i++){int number = target - nums[i];if(hashMap.containsKey(number)){return new int[] {i,hashMap.get(number)};}hashMap.put(nums[i],i);}throw new IllegalArgumentException("No two sum solution");}}
15. 三数之和
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。

class Solution {public List<List<Integer>> threeSum(int[] nums) {Arrays.sort(nums);int n = nums.length;Set<List<Integer>> res = new HashSet<>();for(int i = 0; i< n; i++){int l = i + 1;int r = n - 1;while(l < r){if(nums[i] + nums[l] + nums[r] == 0){res.add(Arrays.asList(nums[i],nums[l],nums[r]));l++;r--;}else if(nums[i] + nums[l] + nums[r] < 0){l++;}else{r--;}}}List<List<Integer>> ans = new ArrayList<>();ans.addAll(res);return ans;}}
53. 最大子序和
给定一个整数数组
nums,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。


class Solution {public int maxSubArray(int[] nums) {int ans = nums[0];int sum = 0;for(int num: nums) {if(sum > 0) {sum += num;} else {sum = num;}ans = Math.max(ans, sum);}return ans;}}
88. 合并两个有序数组
给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。
请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。
注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。


class Solution {public void merge(int[] nums1, int m, int[] nums2, int n) {int len1 = m - 1;int len2 = n - 1;int len = m + n - 1;while(len1 >= 0 && len2 >= 0) {// 注意--符号在后面,表示先进行计算再减1,这种缩写缩短了代码nums1[len--] = nums1[len1] > nums2[len2] ? nums1[len1--] : nums2[len2--];}// 表示将nums2数组从下标0位置开始,拷贝到nums1数组中,从下标0位置开始,长度为len2+1System.arraycopy(nums2, 0, nums1, 0, len2 + 1);}}


class Solution {public void merge(int[] nums1, int m, int[] nums2, int n) {int p1 = 0, p2 = 0;int[] sorted = new int[m + n];int cur;while (p1 < m || p2 < n) {if (p1 == m) {cur = nums2[p2++];} else if (p2 == n) {cur = nums1[p1++];} else if (nums1[p1] < nums2[p2]) {cur = nums1[p1++];} else {cur = nums2[p2++];}sorted[p1 + p2 - 1] = cur;}for (int i = 0; i != m + n; ++i) {nums1[i] = sorted[i];}}}

136. 只出现一次的数字
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。 说明: 你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗? 示例 1: 输入: [2,2,1] 输出: 1
示例 2: 输入: [4,1,2,1,2] 输出: 4



class Solution {public int singleNumber(int[] nums) {int ans = 0;for(int num: nums) {ans ^= num;}return ans; //位运算 异或}}
485. 最大连续 1 的个数
给定一个二进制数组, 计算其中最大连续 1 的个数。 输入:[1,1,0,1,1,1] 输出:3 解释:开头的两位和最后的三位都是连续 1 ,所以最大连续 1 的个数是 3.


class Solution {public int findMaxConsecutiveOnes(int[] nums) {int maxCount = 0, count = 0;int n = nums.length;for (int i = 0; i < n; i++) {if (nums[i] == 1) {count++;} else {maxCount = Math.max(maxCount, count);count = 0;}}maxCount = Math.max(maxCount, count);return maxCount;}}
9. 回文数
给你一个整数 x ,如果 x 是一个回文整数,返回 true ;否则,返回 false 。
回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。例如,121 是回文,而 123 不是。
示例 1: 输入:x = 121 输出:true 示例 2:
输入:x = -121 输出:false 解释:从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。


class Solution {public boolean isPalindrome(int x) {if(x < 0)return false;int cur = 0;int num = x;while(num != 0) {cur = cur * 10 + num % 10;num /= 10;}return cur == x;}}


class Solution {public boolean isPalindrome(int x) {String str_x = String.valueOf(x);StringBuffer sb = new StringBuffer(str_x);String bs = sb.reverse().toString();return bs.equals(str_x);}}













