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

找出数组中重复的数字。

image.png
image.png
image.png
image.png
image.png

  1. class Solution {
  2. public int findRepeatNumber(int[] nums) {
  3. Set<Integer> set = new HashSet<>();
  4. int repeat = -1;
  5. for(int num : nums){
  6. if(!set.add(num)){
  7. repeat = num;
  8. break;
  9. }
  10. }
  11. return repeat;
  12. }
  13. }
  14. class Solution {
  15. public int findRepeatNumber(int[] nums) {
  16. int i = 0;
  17. while(i < nums.length) {
  18. if(nums[i] == i) {
  19. i++;
  20. continue;
  21. }
  22. if(nums[nums[i]] == nums[i]) return nums[i];
  23. int tmp = nums[i];
  24. nums[i] = nums[tmp];
  25. nums[tmp] = tmp;
  26. }
  27. return -1;
  28. }
  29. }

image.png

  1. class Solution {
  2. public int findRepeatNumber(int[] nums) {
  3. int res = 0;
  4. HashMap<Integer, Integer> hashMap = new HashMap<>();
  5. for(int i = 0; i < nums.length; i++){
  6. if(hashMap.containsKey(nums[i])){
  7. res = nums[i];
  8. break;
  9. }else{
  10. hashMap.put(nums[i], 1);
  11. }
  12. }
  13. return res;
  14. }
  15. }

剑指 Offer 04. 二维数组中的查找

在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个高效的函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

image.png

  1. class Solution {
  2. public boolean findNumberIn2DArray(int[][] matrix, int target) {
  3. if(matrix == null || matrix.length == 0 || matrix[0].length == 0){
  4. return false;
  5. }
  6. int rows = matrix.length; //行数
  7. int clos = matrix[0].length; //列数
  8. //左上角起始坐标
  9. int row = 0;
  10. int clo = clos - 1;
  11. while(row < rows && clo >= 0){ //保证不越界
  12. if(matrix[row][clo] == target){
  13. return true;
  14. }else if(matrix[row][clo] > target){
  15. clo--;
  16. }else{
  17. row++;
  18. }
  19. }
  20. return false;
  21. }
  22. }

image.png
image.png
image.png
image.png
image.png
image.png

剑指 Offer 11. 旋转数组的最小数字

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。

image.png
image.png
image.png

  1. class Solution {
  2. public int minArray(int[] numbers) {
  3. int low = 0;
  4. int height = numbers.length - 1;
  5. while(low < height){
  6. int pivot = low + (height - low) / 2;
  7. if(numbers[pivot] < numbers[height]){
  8. height = pivot;
  9. }else if(numbers[pivot] > numbers[height]){
  10. low = pivot + 1;
  11. }else if(numbers[pivot] == numbers[height]){
  12. height--;
  13. }
  14. }
  15. return numbers[low];
  16. }
  17. }

剑指 Offer 17. 打印从1到最大的n位数

输入数字 n,按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3,则打印出 1、2、3 一直到最大的 3 位数 999。

image.png

  1. class Solution {
  2. public int[] printNumbers(int n) {
  3. if(n <= 0){
  4. return new int[0];
  5. }
  6. int max = (int)Math.pow(10, n);
  7. int[] arr = new int[max - 1];
  8. for(int i = 0; i < max - 1; i++){
  9. arr[i] = i + 1;
  10. }
  11. return arr;
  12. }
  13. }

剑指 Offer 21. 调整数组顺序使奇数位于偶数前面

输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分。

image.png

  1. class Solution {
  2. public int[] exchange(int[] nums) {
  3. if(nums == null || nums.length == 0) return nums;
  4. int[] res = new int[nums.length];
  5. int left = 0;
  6. int right = nums.length - 1;
  7. for(int i = 0; i < nums.length; i++){
  8. if(nums[i] % 2 != 0){
  9. res[left++] = nums[i]; //奇数从前往后放
  10. }else{
  11. res[right--] = nums[i]; //偶数从后往前放
  12. }
  13. }
  14. return res;
  15. }
  16. }

剑指 Offer 29. 顺时针打印矩阵

输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。

image.png
image.png

  1. class Solution {
  2. public int[] spiralOrder(int[][] matrix) {
  3. if(matrix.length == 0) return new int[0];
  4. int l = 0; //左边界
  5. int r = matrix[0].length - 1; //右边界
  6. int t = 0; //上边界
  7. int b = matrix.length - 1; //下边界
  8. int x = 0;
  9. int[] res = new int[(r + 1) * (b + 1)];
  10. while(true){
  11. for(int i = l; i <= r; i++) res[x++] = matrix[t][i]; //left to right
  12. if(++t > b) break;
  13. for(int i = t; i <= b; i++) res[x++] = matrix[i][r]; //top to bottom
  14. if(l > --r) break;
  15. for(int i = r; i >= l; i--) res[x++] = matrix[b][i]; //right to left
  16. if(t > --b) break;
  17. for(int i = b; i >= t; i--) res[x++] = matrix[i][l]; //bottom to top
  18. if(++l > r) break;
  19. }
  20. return res;
  21. }
  22. }

剑指 Offer 39. 数组中出现次数超过一半的数字

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。

image.png
image.png
image.png
image.png

  1. //方法3: 摩尔投票法
  2. class Solution {
  3. public int majorityElement(int[] nums) {
  4. int res = 0, rating = 0;
  5. for(int num : nums){
  6. if(rating == 0) res = num;
  7. if(num == res){
  8. rating += 1;
  9. }else{
  10. rating -= 1;
  11. }
  12. }
  13. return res;
  14. }
  15. }
  16. //方法1:哈希表
  17. class Solution {
  18. public int majorityElement(int[] nums) {
  19. Map<Integer, Integer> map = new HashMap<>();
  20. for(int i = 0; i < nums.length; i++){
  21. int count = map.getOrDefault(nums[i], 0 ) + 1;
  22. if(count > nums.length / 2) return nums[i];
  23. map.put(nums[i], count);
  24. }
  25. return -1;
  26. }
  27. }
  28. //方法2:库函数排序
  29. class Solution {
  30. public int majorityElement(int[] nums) {
  31. Arrays.sort(nums);
  32. return nums[nums.length/2];
  33. }
  34. }

剑指 Offer 40. 最小的k个数

输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。

image.png
image.png

  1. //利用快排算法
  2. class Solution {
  3. public int[] getLeastNumbers(int[] arr, int k) {
  4. if(k == 0 || arr.length == 0){
  5. return new int[0];
  6. }
  7. return qsk(arr, 0, arr.length - 1, k - 1);
  8. }
  9. private int[] qsk(int[] nums, int l, int r, int k){
  10. int j = quicksort(nums, l , r);
  11. if(j == k){
  12. return Arrays.copyOf(nums, j+1);
  13. }
  14. return j > k ? qsk(nums, l , j - 1, k): qsk(nums, j+1, r, k);
  15. }
  16. private int quicksort(int[] nums, int l ,int r){
  17. int v = nums[l];
  18. int i = l, j = r + 1;
  19. while(true){
  20. while(++i <= r && nums[i] < v);
  21. while(--j >=l && nums[j] > v);
  22. if(i >= j){
  23. break;
  24. }
  25. int temp = nums[i];
  26. nums[i] = nums[j];
  27. nums[j] = temp;
  28. }
  29. nums[l] = nums[j];
  30. nums[j] = v;
  31. return j; //保证Nums[j] 左边是最小的j个数字
  32. }
  33. }
  34. class Solution {
  35. public int[] getLeastNumbers(int[] arr, int k) {
  36. int[] res = new int[k];
  37. Queue<Integer> queue = new PriorityQueue<>(new Comparator<Integer>(){
  38. @Override
  39. public int compare(Integer o1, Integer o2){
  40. return o2 - o1; //从小到大
  41. }
  42. });
  43. //Queue<Integer> queue = new PriorityQueue<>((o1, o2) -> (o2 - o1));
  44. //大顶堆中保留最小的k个数
  45. for(int i = 0; i < arr.length; i++){
  46. queue.add(arr[i]);
  47. if(queue.size() > k) queue.poll();
  48. }
  49. for(int i = 0; i < k; i++){
  50. res[i] = queue.poll();
  51. }
  52. return res;
  53. }
  54. }

剑指 Offer 42. 连续子数组的最大和

image.png

  1. class Solution {
  2. public int maxSubArray(int[] nums) {
  3. int res = nums[0];
  4. for(int i = 1; i < nums.length; i++){
  5. nums[i] += Math.max(nums[i - 1], 0);
  6. res = Math.max(res, nums[i]);
  7. }
  8. return res;
  9. }
  10. }
  11. class Solution{
  12. public int maxSubArray(int[] nums){
  13. int length = nums.length;
  14. int[] dp = new int[length];
  15. dp[0] = nums[0];
  16. int res = nums[0];
  17. for(int i = 1; i < length; i++){
  18. dp[i] = Math.max(dp[i - 1] + nums[i], nums[i]);
  19. res = Math.max(res, dp[i]);
  20. }
  21. return res;
  22. }
  23. }

剑指 Offer 47. 礼物的最大价值

在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物?

image.png
image.png
image.png
image.png

  1. class Solution {
  2. public int maxValue(int[][] grid) {
  3. int row = grid.length, col = grid[0].length;
  4. int[] dp = new int[col+1];
  5. for(int i = 0; i < row; i++){
  6. for(int j = 1; j < col + 1; j++){
  7. dp[j] = Math.max(dp[j-1], dp[j]) + grid[i][j-1];
  8. }
  9. }
  10. return dp[col];
  11. }
  12. }
  13. class Solution {
  14. public int maxValue(int[][] grid) {
  15. //最经典二维动态规划
  16. //f[i][j] = max(f[i-1][j], f[i][j - 1])
  17. int m = grid.length, n = grid[0].length;
  18. for(int i = 0; i < m; i++){
  19. for(int j = 0; j < n; j++){
  20. if(i == 0 && j == 0) continue;
  21. if( i == 0){
  22. grid[i][j] += grid[i][j - 1];
  23. }
  24. else if(j == 0){
  25. grid[i][j] += grid[i - 1][j];
  26. }else{
  27. grid[i][j] += Math.max(grid[i - 1][j], grid[i][j - 1]);
  28. }
  29. }
  30. }
  31. return grid[m - 1][n - 1];
  32. }
  33. }
  34. class Solution {
  35. public int maxValue(int[][] grid) {
  36. int m = grid.length, n = grid[0].length;
  37. for(int j = 1; j < n; j++) // 初始化第一行
  38. grid[0][j] += grid[0][j - 1];
  39. for(int i = 1; i < m; i++) // 初始化第一列
  40. grid[i][0] += grid[i - 1][0];
  41. for(int i = 1; i < m; i++)
  42. for(int j = 1; j < n; j++)
  43. grid[i][j] += Math.max(grid[i][j - 1], grid[i - 1][j]);
  44. return grid[m - 1][n - 1];
  45. }
  46. }

剑指 Offer 51. 数组中的逆序对

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。

image.png
image.png
image.png

  1. class Solution {
  2. int[] nums, tmp;
  3. public int reversePairs(int[] nums) {
  4. this.nums = nums;
  5. tmp = new int[nums.length];
  6. return mergeSort(0, nums.length - 1);
  7. }
  8. private int mergeSort(int l, int r) {
  9. // 终止条件
  10. if (l >= r) return 0;
  11. // 递归划分
  12. int m = (l + r) / 2;
  13. int res = mergeSort(l, m) + mergeSort(m + 1, r);
  14. // 合并阶段
  15. int i = l, j = m + 1;
  16. for (int k = l; k <= r; k++)
  17. tmp[k] = nums[k];
  18. for (int k = l; k <= r; k++) {
  19. if (i == m + 1)
  20. nums[k] = tmp[j++];
  21. else if (j == r + 1 || tmp[i] <= tmp[j])
  22. nums[k] = tmp[i++];
  23. else {
  24. nums[k] = tmp[j++];
  25. res += m - i + 1; // 统计逆序对
  26. }
  27. }
  28. return res;
  29. }
  30. }

剑指 Offer 53 - I. 在排序数组中查找数字 I

统计一个数字在排序数组中出现的次数。

image.png
image.png
image.png
image.png

  1. class Solution {
  2. public int search(int[] nums, int target) {
  3. if(nums == null || nums.length == 0) return 0;
  4. int start = binarySearch(nums, target);
  5. int end = binarySearch(nums, target + 1);
  6. return end - start + (nums[end] == target ? 1 : 0);
  7. }
  8. private int binarySearch(int[] nums, int tar){
  9. int l = 0;
  10. int r = nums.length - 1;
  11. while(l < r){
  12. int mid = l + (r - l) / 2;
  13. if(nums[mid] < tar){
  14. l = mid + 1;
  15. }else{
  16. r = mid;
  17. }
  18. }
  19. return l;
  20. }
  21. }

image.png
image.png

剑指 Offer 53 - II. 0~n-1中缺失的数字

一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字

image.png
image.png
image.png
image.png
image.png
image.png

剑指 Offer 56 - I. 数组中数字出现的次数

一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。

image.png

  1. class Solution {
  2. public int[] singleNumbers(int[] nums) {
  3. //1.数组中只出现一次的两个数字的异或结果
  4. int sum = 0;
  5. for(int x : nums){
  6. sum ^= x;
  7. }
  8. //2.找到异或二进制中1所在的位置
  9. int index = 0;
  10. while((sum >> index & 1) == 0){
  11. index++;
  12. }
  13. int first = 0;
  14. for(int x : nums){
  15. if((x >> index & 1) == 0){
  16. first ^= x;
  17. }
  18. }
  19. int second = sum ^ first;
  20. return new int[]{first, second};
  21. }
  22. }

image.png
image.png
image.png

剑指 Offer 56 - II. 数组中数字出现的次数 II

在一个数组 nums 中除一个数字只出现一次之外,其他数字都出现了三次。请找出那个只出现一次的数字。

image.png
image.png

  1. class Solution {
  2. public int singleNumber(int[] nums) {
  3. int[] count = new int[32];
  4. for(int num : nums){
  5. for(int i = 0; i < 32; i++){
  6. count[i] += num & 1;
  7. num >>= 1;
  8. }
  9. }
  10. int res = 0, mod = 3;
  11. for(int i = 0; i< 32; i++){
  12. res <<= 1;
  13. res += count[31 - i] % mod;
  14. }
  15. return res;
  16. }
  17. }

剑指 Offer 57. 和为s的两个数字

输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,则输出任意一对即可。

image.png
image.png

  1. class Solution {
  2. public int[] twoSum(int[] nums, int target) {
  3. int i = 0;
  4. int j = nums.length - 1;
  5. while(i < j){
  6. int s = nums[i] + nums[j];
  7. if(s < target){
  8. i++;
  9. }else if(s > target){
  10. j--;
  11. }else{
  12. return new int[]{nums[i],nums[j]};
  13. }
  14. }
  15. return null;
  16. }
  17. }
  18. class Solution{
  19. public int[] twoSum(int[] nums, int target){
  20. Set<Integer> s = new HashSet<>();
  21. for(int i = 0; i < nums.length; i++){
  22. s.add(nums[i]);
  23. }
  24. for(int i = 0; i < nums.length; i++){
  25. if(s.contains(target - nums[i])){
  26. return new int[]{nums[i], target - nums[i]};
  27. }
  28. }
  29. return null;
  30. }
  31. }

剑指 Offer 61. 扑克牌中的顺子

从扑克牌中随机抽5张牌,判断是不是一个顺子,即这5张牌是不是连续的。2~10为数字本身,A为1,J为11,Q为12,K为13,而大、小王为 0 ,可以看成任意数字。A 不能视为 14。

image.png

1. 两数之和

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标 你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。 你可以按任意顺序返回答案。

image.png
image.png

  1. class Solution {
  2. public int[] twoSum(int[] nums, int target) {
  3. Map <Integer,Integer> hashMap = new HashMap<>();
  4. for(int i=0;i<nums.length;i++){
  5. int number = target - nums[i];
  6. if(hashMap.containsKey(number)){
  7. return new int[] {i,hashMap.get(number)};
  8. }
  9. hashMap.put(nums[i],i);
  10. }
  11. throw new IllegalArgumentException("No two sum solution");
  12. }
  13. }

15. 三数之和

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。

image.png

  1. class Solution {
  2. public List<List<Integer>> threeSum(int[] nums) {
  3. Arrays.sort(nums);
  4. int n = nums.length;
  5. Set<List<Integer>> res = new HashSet<>();
  6. for(int i = 0; i< n; i++){
  7. int l = i + 1;
  8. int r = n - 1;
  9. while(l < r){
  10. if(nums[i] + nums[l] + nums[r] == 0){
  11. res.add(Arrays.asList(nums[i],nums[l],nums[r]));
  12. l++;
  13. r--;
  14. }else if(nums[i] + nums[l] + nums[r] < 0){
  15. l++;
  16. }else{
  17. r--;
  18. }
  19. }
  20. }
  21. List<List<Integer>> ans = new ArrayList<>();
  22. ans.addAll(res);
  23. return ans;
  24. }
  25. }

53. 最大子序和

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

image.png
image.png

  1. class Solution {
  2. public int maxSubArray(int[] nums) {
  3. int ans = nums[0];
  4. int sum = 0;
  5. for(int num: nums) {
  6. if(sum > 0) {
  7. sum += num;
  8. } else {
  9. sum = num;
  10. }
  11. ans = Math.max(ans, sum);
  12. }
  13. return ans;
  14. }
  15. }

88. 合并两个有序数组

给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。

请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。

注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。

image.png
image.png

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

image.png
image.png

  1. class Solution {
  2. public void merge(int[] nums1, int m, int[] nums2, int n) {
  3. int p1 = 0, p2 = 0;
  4. int[] sorted = new int[m + n];
  5. int cur;
  6. while (p1 < m || p2 < n) {
  7. if (p1 == m) {
  8. cur = nums2[p2++];
  9. } else if (p2 == n) {
  10. cur = nums1[p1++];
  11. } else if (nums1[p1] < nums2[p2]) {
  12. cur = nums1[p1++];
  13. } else {
  14. cur = nums2[p2++];
  15. }
  16. sorted[p1 + p2 - 1] = cur;
  17. }
  18. for (int i = 0; i != m + n; ++i) {
  19. nums1[i] = sorted[i];
  20. }
  21. }
  22. }

image.png

136. 只出现一次的数字

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

示例 2: 输入: [4,1,2,1,2] 输出: 4

image.png
image.png
image.png

  1. class Solution {
  2. public int singleNumber(int[] nums) {
  3. int ans = 0;
  4. for(int num: nums) {
  5. ans ^= num;
  6. }
  7. return ans; //位运算 异或
  8. }
  9. }

485. 最大连续 1 的个数

给定一个二进制数组, 计算其中最大连续 1 的个数。 输入:[1,1,0,1,1,1] 输出:3 解释:开头的两位和最后的三位都是连续 1 ,所以最大连续 1 的个数是 3.

image.png
image.png

  1. class Solution {
  2. public int findMaxConsecutiveOnes(int[] nums) {
  3. int maxCount = 0, count = 0;
  4. int n = nums.length;
  5. for (int i = 0; i < n; i++) {
  6. if (nums[i] == 1) {
  7. count++;
  8. } else {
  9. maxCount = Math.max(maxCount, count);
  10. count = 0;
  11. }
  12. }
  13. maxCount = Math.max(maxCount, count);
  14. return maxCount;
  15. }
  16. }

9. 回文数

给你一个整数 x ,如果 x 是一个回文整数,返回 true ;否则,返回 false 。

回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。例如,121 是回文,而 123 不是。

示例 1: 输入:x = 121 输出:true 示例 2:

输入:x = -121 输出:false 解释:从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。

image.png
image.png

  1. class Solution {
  2. public boolean isPalindrome(int x) {
  3. if(x < 0)
  4. return false;
  5. int cur = 0;
  6. int num = x;
  7. while(num != 0) {
  8. cur = cur * 10 + num % 10;
  9. num /= 10;
  10. }
  11. return cur == x;
  12. }
  13. }

image.png
image.png

  1. class Solution {
  2. public boolean isPalindrome(int x) {
  3. String str_x = String.valueOf(x);
  4. StringBuffer sb = new StringBuffer(str_x);
  5. String bs = sb.reverse().toString();
  6. return bs.equals(str_x);
  7. }
  8. }