代码随想录学习记录 数组 双指针思想
二分查找 这道题目的前提是数组为有序数组 ,同时题目还强调数组中无重复元素 ,因为一旦有重复元素,使用二分查找法返回的元素下标可能不是唯一的,这些都是使用二分法的前提条件,当大家看到题目描述满足如上条件的时候,可要想一想是不是可以用二分法了。
704. 二分查找
704. 二分查找 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Solution { public int search (int [] nums, int target) { int left = 0 , right = nums.length - 1 ; while (left <= right) { int mid = left + ((right - left) >> 1 ); if (nums[mid] < target) { left = mid + 1 ; } else if (nums[mid] > target) { right = mid - 1 ; } else { return mid; } } return -1 ; } }
35. 搜索插入位置
35. 搜索插入位置 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Solution { public int searchInsert (int [] nums, int target) { int left = 0 , right = nums.length - 1 ; while (left <= right) { int mid = left + ((right - left) >> 1 ); if (nums[mid] < target) { left = mid + 1 ; } else if (nums[mid] > target) { right = mid - 1 ; } else { return mid; } } return left; } }
34. 在排序数组中查找元素的第一个和最后一个位置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 class Solution { public int [] searchRange(int [] nums, int target) { int left = 0 , right = nums.length - 1 ; while (left <= right) { int mid = left + ((right - left) >> 1 ); if (nums[mid] < target) { left = mid + 1 ; } else if (nums[mid] > target) { right = mid - 1 ; } else { if (nums[left] != nums[mid]) { left++; } else if (nums[right] != nums[mid]) { right--; } else { return new int [] { left, right }; } } } return new int [] { -1 , -1 }; } }
**69. x 的平方根 **
69. x 的平方根 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Solution { public int mySqrt (int x) { int left = 0 , right = x, result = -1 ; while (left <= right) { int mid = left + ((right - left) >> 1 ); long square = (long ) mid * mid; if (square < x) { left = mid + 1 ; } else if (square > x) { right = mid - 1 ; } else { return mid; } } return right; } }
367. 有效的完全平方数
367. 有效的完全平方数 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Solution { public boolean isPerfectSquare (int num) { int left = 0 , right = num; while (left <= right) { int mid = left + ((right - left) >> 1 ); if ((long ) mid * mid < num) { left = mid + 1 ; } else if ((long ) mid * mid > num) { right = mid - 1 ; } else { return true ; } } return false ; } }
双指针
27. 移除元素
27. 移除元素 - 力扣(LeetCode)
双指针解决,快慢指针
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Solution { public int removeElement (int [] nums, int val) { int count = 0 ; int index = 0 ; int i = 0 ; while (i <= nums.length - 1 ) { if (nums[i] == val) { i++; } else { nums[index++] = nums[i++]; count++; } } return count; } }
26. 删除有序数组中的重复项
26. 删除有序数组中的重复项 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Solution { public int removeDuplicates (int [] nums) { if (nums.length==0 ){ return 0 ; } int count = 1 ; int lastDifferent = nums[0 ]; int slow = 1 ; for (int fast = 1 ;fast<nums.length;fast++){ if (nums[fast]!=lastDifferent){ lastDifferent = nums[fast]; nums[slow++] = nums[fast]; count++; } } return count; } }
283. 移动零
283. 移动零 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Solution { public void moveZeroes (int [] nums) { int slow = 0 ; for (int fast = 0 ; fast < nums.length; fast++) { if (nums[fast] != 0 ) { nums[slow++] = nums[fast]; } } while (slow <= nums.length - 1 ) { nums[slow++] = 0 ; } } }
844. 比较含退格的字符串
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 class Solution { public boolean backspaceCompare (String s, String t) { int i = s.length() - 1 , j = t.length() - 1 ; int skipS = 0 , skipT = 0 ; while (i >= 0 || j >= 0 ) { while (i >= 0 ) { if (s.charAt(i) == '#' ) { skipS++; i--; } else if (skipS > 0 ) { skipS--; i--; } else { break ; } } while (j >= 0 ) { if (t.charAt(j) == '#' ) { skipT++; j--; } else if (skipT > 0 ) { skipT--; j--; } else { break ; } } if (i >= 0 && j >= 0 ) { if (s.charAt(i) != t.charAt(j)) { return false ; } } else { if (i >= 0 || j >= 0 ) { return false ; } } i--; j--; } return true ; } }
977. 有序数组的平方
977. 有序数组的平方 - 力扣(LeetCode)
还可以就是先全部平方,然后一个sort搞定,就是原地操作了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Solution { public int [] sortedSquares(int [] nums) { int left = 0 , right = nums.length - 1 ; int [] result = new int [nums.length]; int i = result.length - 1 ; while (left <= right) { if (nums[left] * nums[left] < nums[right] * nums[right]) { result[i--] = nums[right] * nums[right]; right--; } else { result[i--] = nums[left] * nums[left]; left++; } } return result; } }
滑动窗口
209. 长度最小的子数组
209. 长度最小的子数组 - 力扣(LeetCode)
在暴力解法中,是一个for循环滑动窗口的起始位置,一个for循环为滑动窗口的终止位置,用两个for循环 完成了一个不断搜索区间的过程。
那么滑动窗口如何用一个for循环来完成这个操作呢。
首先要思考 如果用一个for循环,那么应该表示 滑动窗口的起始位置,还是终止位置。
如果只用一个for循环来表示 滑动窗口的起始位置,那么如何遍历剩下的终止位置?
此时难免再次陷入 暴力解法的怪圈。
所以 只用一个for循环,那么这个循环的索引,一定是表示 滑动窗口的终止位置。
窗口就是 满足其和 ≥ s 的长度最小的 连续 子数组。
窗口的起始位置如何移动:如果当前窗口的值大于等于s了,窗口就要向前移动了(也就是该缩小了)。
窗口的结束位置如何移动:窗口的结束位置就是遍历数组的指针,也就是for循环里的索引。
可以发现滑动窗口的精妙之处在于根据当前子序列和大小的情况,不断调节子序列的起始位置。从而将O(n^2)暴力解法降为O(n)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Solution { public int minSubArrayLen (int target, int [] nums) { int result = Integer.MAX_VALUE; int sum = 0 ; int left = 0 ; for (int right = 0 ;right<nums.length;right++){ sum+=nums[right]; while (sum>=target){ result = Math.min(result,right-left+1 ); sum-=nums[left++]; } } return result == Integer.MAX_VALUE ? 0 : result; } }
904. 水果成篮
904. 水果成篮 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Solution { public int totalFruit (int [] fruits) { int result = 0 ; int left = 0 ; HashMap<Integer, Integer> map = new HashMap <>(); for (int right = 0 ; right < fruits.length; right++) { map.put(fruits[right], map.getOrDefault(fruits[right], 0 ) + 1 ); while (map.size() > 2 ) { map.put(fruits[left], map.get(fruits[left]) - 1 ); if (map.get(fruits[left]) == 0 ) { map.remove(fruits[left]); } ++left; } result = Math.max(result, right - left + 1 ); } return result; } }
76. 最小覆盖子串
76. 最小覆盖子串 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 class Solution { HashMap<Character, Integer> map1 = new HashMap <>(); HashMap<Character, Integer> map2 = new HashMap <>(); public String minWindow (String s, String t) { for (int i = 0 ; i < t.length(); i++) { map1.put(t.charAt(i), map1.getOrDefault(t.charAt(i), 0 ) + 1 ); } int left = 0 ; int len = Integer.MAX_VALUE, l = -1 , r = -1 ; for (int right = 0 ; right < s.length(); right++) { if (map1.containsKey(s.charAt(right))) { map2.put(s.charAt(right), map2.getOrDefault(s.charAt(right), 0 ) + 1 ); } while (check() && left <= right) { if (right - left + 1 < len) { len = right - left + 1 ; l = left; r = right; } if (map1.containsKey(s.charAt(left))) { map2.put(s.charAt(left), map2.getOrDefault(s.charAt(left), 0 ) - 1 ); } left++; } } return l == -1 ? "" : s.substring(l, r + 1 ); } public boolean check () { for (Map.Entry<Character, Integer> entry : map1.entrySet()) { if (map2.getOrDefault(entry.getKey(), 0 ) < entry.getValue()) { return false ; } } return true ; } }
模拟 螺旋
59. 螺旋矩阵 II
59. 螺旋矩阵 II - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 class Solution { public int [][] generateMatrix(int n) { int [][] result = new int [n][n]; int top = 0 , bottom = n - 1 , left = 0 , right = n - 1 ; int i = 0 , j = 0 , temp = 1 ; while (top <= bottom && left <= right) { for (int k = left; k <= right; k++) { result[top][k] = temp++; } top++; for (int k = top; k <= bottom; k++) { result[k][right] = temp++; } right--; for (int k = right; k >= left; k--) { result[bottom][k] = temp++; } bottom--; for (int k = bottom; k >= top; k--) { result[k][left] = temp++; } left++; } return result; } }
54. 螺旋矩阵
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 class Solution { public List<Integer> spiralOrder (int [][] matrix) { int row = matrix.length; if (row == 0 ) { return new ArrayList <Integer>(); } int column = matrix[0 ].length; List<Integer> result = new ArrayList <>(); int top = 0 , bottom = row - 1 , left = 0 , right = column - 1 ; while (top <= bottom && left <= right) { for (int k = left; k <= right; k++) { result.add(matrix[top][k]); } top++; for (int k = top; k <= bottom; k++) { result.add(matrix[k][right]); } right--; if (top <= bottom) { for (int k = right; k >= left; k--) { result.add(matrix[bottom][k]); } bottom--; } if (left <= right) { for (int k = bottom; k >= top; k--) { result.add(matrix[k][left]); } left++; } } return result; } }
LCR 146. 螺旋遍历二维数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 class Solution { public int [] spiralArray(int [][] array) { int row = array.length; if (row == 0 ) { return new int [0 ]; } int column = array[0 ].length; int [] result = new int [row * column]; int top = 0 , bottom = row - 1 , left = 0 , right = column - 1 ; int temp = 0 ; while (top <= bottom && left <= right) { for (int k = left; k <= right; k++) { result[temp++] = array[top][k]; } top++; for (int k = top; k <= bottom; k++) { result[temp++] = array[k][right]; } right--; if (top <= bottom) { for (int k = right; k >= left; k--) { result[temp++] = array[bottom][k]; } bottom--; } if (left <= right) { for (int k = bottom; k >= top; k--) { result[temp++] = array[k][left]; } left++; } } return result; } }
前缀和 区间和
第一行输入为整数数组 Array 的长度 n,接下来 n 行,每行一个整数,表示数组的元素。随后的输入为需要计算总和的区间下标:a,b (b > = a),直至文件结束。
如果,我们想统计,在vec数组上 下标 2 到下标 5 之间的累加和,那是不是就用 p[5] - p[1] 就可以了。
为什么呢?
p[1] = vec[0] + vec[1]; p[5] = vec[0] + vec[1] + vec[2] + vec[3] + vec[4] + vec[5]; p[5] - p[1] = vec[2] + vec[3] + vec[4] + vec[5];
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 import java.util.Scanner;public class Main { public static void main (String[] args) { Scanner scanner = new Scanner (System.in); int n = scanner.nextInt(); int [] vec = new int [n]; int [] p = new int [n]; int presum = 0 ; for (int i = 0 ; i < n; i++) { vec[i] = scanner.nextInt(); presum += vec[i]; p[i] = presum; } while (scanner.hasNextInt()) { int a = scanner.nextInt(); int b = scanner.nextInt(); int sum; if (a == 0 ) { sum = p[b]; } else { sum = p[b] - p[a - 1 ]; } System.out.println(sum); } scanner.close(); } }
开发商购买土地
在一个城市区域内,被划分成了n * m个连续的区块,每个区块都拥有不同的权值,代表着其土地价值。目前,有两家开发公司,A 公司和 B 公司,希望购买这个城市区域的土地。
现在,需要将这个城市区域的所有区块分配给 A 公司和 B 公司。
然而,由于城市规划的限制,只允许将区域按横向或纵向划分成两个子区域,而且每个子区域都必须包含一个或多个区块。
为了确保公平竞争,你需要找到一种分配方式,使得 A 公司和 B 公司各自的子区域内的土地总价值之差最小。
注意:区块不可再分。
【输入描述】
第一行输入两个正整数,代表 n 和 m。
接下来的 n 行,每行输出 m 个正整数。
输出描述
请输出一个整数,代表两个子区域内土地总价值之间的最小差距。
【输入示例】
3 3 1 2 3 2 1 3 1 2 3
【输出示例】
0
【提示信息】
如果将区域按照如下方式划分:
1 2 | 3 2 1 | 3 1 2 | 3
两个子区域内土地总价值之间的最小差距可以达到 0。
【数据范围】:
1 <= n, m <= 100;
n 和 m 不同时为 1。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 import java.util.Scanner;public class Main { public static void main (String[] args) { Scanner scanner = new Scanner (System.in); int n = scanner.nextInt(); int m = scanner.nextInt(); int sum = 0 ; int [][] vec = new int [n][m]; for (int i = 0 ; i < n; i++) { for (int j = 0 ; j < m; j++) { vec[i][j] = scanner.nextInt(); sum += vec[i][j]; } } int [] horizontal = new int [n]; for (int i = 0 ; i < n; i++) { for (int j = 0 ; j < m; j++) { horizontal[i] += vec[i][j]; } } int [] vertical = new int [m]; for (int j = 0 ; j < m; j++) { for (int i = 0 ; i < n; i++) { vertical[j] += vec[i][j]; } } int result = Integer.MAX_VALUE; int horizontalCut = 0 ; for (int i = 0 ; i < n; i++) { horizontalCut += horizontal[i]; result = Math.min(result, Math.abs(sum - 2 * horizontalCut)); } int verticalCut = 0 ; for (int j = 0 ; j < m; j++) { verticalCut += vertical[j]; result = Math.min(result, Math.abs(sum - 2 * verticalCut)); } System.out.println(result); scanner.close(); } }