《C++算法竞赛攻略:从入门到降维打击》——第8章

第八章:双指针与滑动窗口

在处理数组或字符串的许多问题中,我们常常会不自觉地写出嵌套循环,形成 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)

双指针思想的核心在于:通过利用问题的某些内在特性(如单调性),让两个指针ij进行方向性、协同性的移动,而不是毫无章法地回溯。两个指针在整个过程中只会从头到尾各扫描一遍数组,总的移动次数是线性的,从而将时间复杂度优化到 O(N)O(N)O(N)

这种思想主要分为两种经典的模型:对撞指针同向指针(滑动窗口),它们分别适用于不同类型的问题。

8.2 C++实现:同向指针、对撞指针的模型

1. 对撞指针 (Colliding Pointers)

对撞指针,顾名思义,是指两个指针分别位于序列的两端,然后相向移动,直至“对撞”(相遇或交错)。这种模型特别适用于已排序的序列,利用有序性来缩小解的搜索空间。

模型框架:

  • 一个左指针 left 从序列头部(索引0)开始。
  • 一个右指针 right 从序列尾部(索引n-1)开始。
  • 在一个 while (left < right) 循环中,根据 a[left]a[right] 的某种组合关系,决定如何移动指针:
    • 如果满足条件,可能同时移动 leftright
    • 如果不满足,根据判断只移动其中一个指针,以逼近目标。

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(慢指针)在必要时也向右移动,以收缩范围。这两个指针 ij 之间形成了一个动态变化的“窗口”,因此该模型也被形象地称为“滑动窗口”。

这种模型非常适合解决连续子序列相关的问题,例如“最长连续无重复子串”、“长度最小的连续子数组”等。

模型框架:

  • 一个慢指针 i 和一个快指针 j 都从索引0开始。
  • 外层循环由快指针 j 驱动,不断扩大窗口的右边界 for (int j = 0; j < n; ++j)
  • 在循环体内,将新元素 a[j] 纳入窗口,并更新窗口状态。
  • 内层使用 while 循环判断当前窗口是否不再满足某个约
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱看烟花的码农

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值