第八章:双指针与滑动窗口
在处理数组或字符串的许多问题中,我们常常会不自觉地写出嵌套循环,形成 O(N2)O(N^2)O(N2) 的时间复杂度。然而,其中大部分问题都存在一种精妙的优化方法,能将复杂度降低到线性时间 O(N)O(N)O(N),这就是双指针技术。双指针不是一个特定的算法,而是一种通用的编程思想和技巧,是优化暴力枚举的利器。
8.1 思想:优化 O(N2)O(N^2)O(N2) 遍历的利器
想象一下,在一个队伍(数组)中寻找符合某种条件的两个人。最朴素的方法是,让第一个人(指针i)不动,第二个人(指针j)从他后面逐个看过去;然后再让第一个人前进一位,第二个人再从新位置后面看一遍……这就是一个典型的嵌套循环,时间复杂度为 O(N2)O(N^2)O(N2)。
双指针思想的核心在于:通过利用问题的某些内在特性(如单调性),让两个指针i和j进行方向性、协同性的移动,而不是毫无章法地回溯。两个指针在整个过程中只会从头到尾各扫描一遍数组,总的移动次数是线性的,从而将时间复杂度优化到 O(N)O(N)O(N)。
这种思想主要分为两种经典的模型:对撞指针和同向指针(滑动窗口),它们分别适用于不同类型的问题。
8.2 C++实现:同向指针、对撞指针的模型
1. 对撞指针 (Colliding Pointers)
对撞指针,顾名思义,是指两个指针分别位于序列的两端,然后相向移动,直至“对撞”(相遇或交错)。这种模型特别适用于已排序的序列,利用有序性来缩小解的搜索空间。
模型框架:
- 一个左指针
left从序列头部(索引0)开始。 - 一个右指针
right从序列尾部(索引n-1)开始。 - 在一个
while (left < right)循环中,根据a[left]和a[right]的某种组合关系,决定如何移动指针:- 如果满足条件,可能同时移动
left和right。 - 如果不满足,根据判断只移动其中一个指针,以逼近目标。
- 如果满足条件,可能同时移动
C++代码模板:
// 适用于已排序的数组 a
int left = 0, right = a.size() - 1;
while (left < right) {
// 根据 a[left] 和 a[right] 的关系进行判断
if (/* condition is met */) {
// 找到一个解,或者更新结果
// 然后根据题意移动指针,例如:
left++;
right--;
} else if (/* some other condition, e.g., sum is too small */) {
// 需要增大结果,移动左指针
left++;
} else {
// e.g., sum is too large
// 需要减小结果,移动右指针
right--;
}
}
2. 同向指针 (Same-Direction Pointers) / 滑动窗口 (Sliding Window)
同向指针是指两个指针都从序列的同一端(通常是头部)开始,并以相同的方向前进。其中一个指针 j(快指针)负责向右拓展探索,另一个指针 i(慢指针)在必要时也向右移动,以收缩范围。这两个指针 i 和 j 之间形成了一个动态变化的“窗口”,因此该模型也被形象地称为“滑动窗口”。
这种模型非常适合解决连续子序列相关的问题,例如“最长连续无重复子串”、“长度最小的连续子数组”等。
模型框架:
- 一个慢指针
i和一个快指针j都从索引0开始。 - 外层循环由快指针
j驱动,不断扩大窗口的右边界for (int j = 0; j < n; ++j)。 - 在循环体内,将新元素
a[j]纳入窗口,并更新窗口状态。 - 内层使用
while循环判断当前窗口是否不再满足某个约

最低0.47元/天 解锁文章
638

被折叠的 条评论
为什么被折叠?



