数组和双指针框架
- 快慢指针:有序数组/链表原地去重、数组/链表原地删除
- 快慢窗口指针:在限定条件下找最长/短的连续子序列/子串/子数组
- 左右最值指针:缩减一维/二维有序搜索空间
快慢指针:有序数组/链表原地去重、数组/链表原地删除
题目:26.删除有序数组中的重复项
核心模式:
- 数组已经排序,所以重复的元素一定连在一起
初始化一个慢指针和一个快指针都为0,慢指针记录最终的结果,快指针遍历整个数组。
快指针fast从数组第一个元素遍历到数组的最后一个元素,当快指针找到和慢指针不重复的值,就把fast的值赋予slow(不重复的数组添加到最终结果),并且slow指向下一个元素,最后 slow 就是需要的结果。
练习:第 83 题「删除排序链表中的重复元素」
如果给你一个有序的单链表,如何去重呢?
其实和数组去重是一模一样的,唯一的区别是把数组赋值操作变成操作指针而已。
题目:27.移除元素
原地删除也使用快慢指针。
初始化一个慢指针和一个快指针都为0,慢指针记录最终的结果,快指针遍历整个数组。
快指针fast从数组第一个元素遍历到数组的最后一个元素,当快指针找到的不是需要删除的值,就把fast的值赋予slow(没被删除的元素添加到最终结果),并且slow指向下一个元素,最后 slow 就是需要的结果。
快慢窗口指针:在限定条件下找最长/短的连续子序列/子串/子数组
滑动窗口:
- 3.无重复字符的最长子串
- 209.长度最小的子数组
- 219.存在重复元素 II
- [220].存在重复元素 III
/* 滑动窗口算法框架 */
void slidingWindow(string s, string t) {
unordered_map<char, int> need, window;
for (char c : t) need[c]++;
int left = 0, right = 0;
int valid = 0;
while (right < s.size()) {
char c = s[right]; // c 是将移入窗口的字符
right++; // 右移(增大)窗口
// 进行窗口内数据的一系列更新
while (window needs shrink) {
char d = s[left]; // d 是将移出窗口的字符
left++; // 左移(缩小)窗口
}
}
}
左右最值指针:缩减一维/二维有序搜索空间
左右指针实现的二分查找,能缩减一维有序数组的搜索空间,每次排除一半的元素来减少比较的次数。
因为一维有序数组的二分查找,太普及了,就不额外写了。
可在二维有序空间中却不能使用二分查找。
二分搜索的中间点,显然应该是矩阵的中心点:
这个中心点把矩阵分成的四个部分中,只有左上部分全部小于中心点,只有右下部分全部大于中心点。也就是说做一次比较最多只能削减 1/4 的搜索空间。更大的问题是,删除掉左上部分或者右下部分之后,剩余的形状不再是一个规则的矩形,就无法继续进行二分搜索了。
二分查找只能做左上、右下的切分,左下、右上不能二分。
其实左右指针的搜索算法,不止可以有一维的二分搜索,还有二维的行列搜索,每次排除一行/列元素来减少比较的次数。
题目:167.俩数之和
使用了两个指针,一个只向左移动,一个只向右移动。走完一趟,就得到了答案。
具体解析:
- https://leetcode.cn/problems/two-sum-ii-input-array-is-sorted/solution/yi-zhang-tu-gao-su-ni-on-de-shuang-zhi-zhen-jie-fa/
- 特征:缩减二维有序搜索空间,方法:左右最值指针