代码随想录-数组
数组理论基础
704.二分查找
-
二分查找算法
二分查找算法使用前提:有序数组、无重复元素
循环不变量:注意要先固定好区间之后再考虑while和if中条件的具体操作
二分查找算法可以尝试将O(N)的时间复杂度优化为O(logN)
-
暴力解法时间复杂度:O(n)
二分法时间复杂度:O(logn)
(在判断完nums[mid]后,左右边界应该都不再考虑 nums[mid] 项了。对于左闭右闭区间和左闭右开区间,只是单纯因为区间开闭不同,所以跳过nums[mid]的方式不同)
-
自定义标准 [left, right) 写法
时刻牢记区间为左闭右开。[left, right)int left=0; int right = nums.length; //因为是[left,right)区间 while( left < right ){ //因为是[left,right)区间,left==right是不合理情况,所以写left < right mid = (left+right)/2; if(nums[mid] > target){ right = mid; //说明整体区间偏大,右边界right向左收 } else if (nums[mid] < target){ left = mid+1; //说明整体区间偏小,左边界left向右收 } else return mid; } return -1
27.移除元素
-
双指针法(快慢指针法)
快指针走整个循环,慢指针记位置,直接一个定点复制覆盖,元素相对位置不变。
在for循环中写if。由于不满足if条件时会跳过if中的操作,会导致快慢指针出现差距,是解题要点。
通常在if中写普通操作,if的条件用于排除特殊情况 -
暴力解法时间复杂度:O(n^2)
双指针时间复杂度:O(n) -
自定义标准写法
int slow=0; for(int fast=0; fast<nums.length; fast++){ if(nums[fast]!=val){ //只有满足if中的条件时,fast指针和slow指针都往前走。不满足if中的条件时,跳过if中的操作,导致fast和slow出现差距 nums[slow++]=nums[fast]; } } return slow; //此时slow经过++操作正好等于新数组长度,返回新数组长度
977.有序数组的平方
-
双指针法(对撞指针)
对撞指针。有序数组的有序操作都可以考虑一下双指针
-
时间复杂度:O(N)
-
自定义标准写法
for(int i=0, j=nums.length-1; i<=j; ){ //i<=j是因为要考虑到最后i和j同时指向的那个元素 //对撞过程,满足条件的就移动 }
209.长度最小的子数组
-
双指针法(滑动窗口)
作为窗口右边的指针正常循环,每次往后走一个。窗口左边的指针在一次循环内部做while循环判断有效降低时间复杂度为O(2N)。因为数组中每个数只需要经历进入窗口和出窗口两个操作
-
暴力解法时间复杂度:O(n^2)
滑动窗口时间复杂度:O(n) -
自定义标准写法
三元运算符:条件表达式 ? 表达式1 : 表达式2
问号前面的位置是判断的条件,判断结果为bool型,为true时调用表达式1,为false时调用表达式2。for(int left=0, right=0; right < nums.length; right++){ sum += nums[right]; //计算条件 while (sum >= s) { result = Math.min(result, right - left + 1); //运算并记录当前情况 sum -= nums[left++]; //最后左指针右移,试图缩小窗口 } } return (result == Integer.MAX_VALUE) ? 0 : result;
59.螺旋矩阵2
-
思路:
2. 确定循环条件是什么(这里最外层循环是按圈计数的)
3. 确定下每次循环中会改变的量(这里每一圈的起始位置[startx][starty]
4. 确定内层循环不变条件(这里指每一条边的循环方式,都是包括头不包括尾)
循环不变量:要确定在循环过程中始终不变的循环条件,要有统一的规则,例如如何操作这四个边。
对于复杂的循环,可以边写边声明变量
58.区间和
使用暴力解的话,如果查询m次的话,算法时间复杂度为 O(n * m) m
-
前缀和法:在涉及计算区间和的问题时非常有用!
前缀和的思想是重复利用计算过的子数组之和,从而降低区间查询需要累加计算的次数。 -
自定义标准写法
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(); } }
44.开发商购买土地
附录
- 二分查找法 [left, right] 写法
时刻牢记区间定义为左闭右闭int left=0; int right=nums.length-1; //因为是【left,right】区间,right需要从有意义的数组开始 while( left <= right ){ //因为是【left,right】区间,left==right是合理情况需要考虑,所以写left<=right middle=(left+right)/2; if(target < nums[middle]){ right=middle-1; //因为是【left,right】区间,当target已经确认位于nums数组的左半侧且小于此时的nums[middle]时,直接令right=middle-1,排除nums[middle]。 } else if (target > nums[middle]){ left=middle+1; //因为是【left,right】区间,当target已经确认位于nums数组的右半侧且大于此时的nums[middle]时,直接令left=middle+1,排除nums[middle]。 } else return middle; } return -1