从今天开始二刷代码随想录,想着做一些阶段性的总结,之前一刷的时候只是每一天会做一些记录,但没有对某一章节的整理,现在从数组篇开始,相当于是做笔记了,以后也方便自己回顾。
数组篇主要包含的重要知识点为二分法,双指针,以及滑动窗口。
1、二分法
以LC_704为例
注意,二分法的适用前提是数组一定是严格有序的。
二分法用于数组中快速搜索某一元素,需要定义左右边界以及中间下标middle
int left = 0;
int right = nums.length;
int middle; // 左闭右开
二分法的核心就是在循环中动态更新middle的值,使搜索范围不断减小
middle = left + ((right - left) >> 1); // 每次更新middle 取right和left的中点
之后就是比较目标值与nums[middle]的大小关系,并动态更新middle
完整代码如下,要注意边界问题,如果是左闭右开的话,while循环里一定是left < right,因为left==right是没有意义的。
class Solution {
public int search(int[] nums, int target) {
if (target < nums[0] || target > nums[nums.length - 1]) return -1;
int left = 0;
int right = nums.length;
int middle; // 左闭右开
while (left < right) {
middle = left + ((right - left) >> 1); // 每次更新middle
if (target > nums[middle]) {
// nums[middle]比target小 下一次左边界为middle + 1 因为是闭区间 不能取middle
left = middle + 1;
}else if (target < nums[middle]) {
// nums[middle]比target大 下一次右边界为middle,因为开区间取不到middle
right = middle;
}else return middle; // 相等 直接返回该下标
}
return -1; // 没找到
}
}
2、双指针
双指针涉及的题目类型有很多,滑动窗口本质也是双指针
这里就以LC_27为例
双指针的重点是搞清楚,两个指针什么时候该动,什么时候不动。
这道题使用的是快慢指针,也是双指针的一种
做法是fast指针自然向后移动,low指针移动的时机为,当nums[fast] != val时,移动low,并进行操作
完整代码如下,边界使用的是左闭右闭。
class Solution {
public int removeElement(int[] nums, int val) {
int low = 0; // 快慢指针
int fast = 0;
while (fast < nums.length) {
if (val != nums[fast]) { // 一样就fast向前 不一样就将fast的值赋给low的值 再让low向前
nums[low++] = nums[fast];
}
fast++;
}
return low;
}
}
该代码的逻辑用卡哥提供的动图解释非常清晰。
3、滑动窗口
滑动窗口本质也是双指针,只不过重点处理的是双指针之间的值,所以称为滑动窗口。
以LC_209为例
sum为窗口内的数值之和,当sum大于等于target时,说明需要慢指针不断向右移以减小sum的值,同时缩短窗口的长度;当sum小于target时,说明需要快指针不断向右移动以增加sum的值,以得到sum=target的子数组的窗口。
sum的变化一定与窗口的变化是同步的。
因为要获得的是最小长度的子数组,所以每次缩短窗口时都需要更新窗口大小。
完整代码如下
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int i = 0; // 滑动窗口 或叫双指针
int j = 0;
int sum = 0; // 累计和
int subArrLen = 0; // 每次保存j - i的值 即窗口长度
int res = Integer.MAX_VALUE; // 结果
while (j < nums.length) {
// 内部两个循环模拟窗口移动
while (sum < target && j < nums.length) { // 要用while
sum += nums[j];
j++;
}
while (sum >= target) { // 要用while
subArrLen = j - i;
res = Math.min(res, subArrLen); // 取最小的长度
sum -= nums[i]; // 左窗口准备移动 要先减去i对应加的值
i++;
}
}
return res == Integer.MAX_VALUE ? 0 : res;
}
}
依然要注意边界,这里使用的是左闭右闭。