前言
双指针算法,有时也被称为尺取法或滑动窗口,是一种优化暴力枚举策略的有效手段。当我们在两层 for 循环的暴力枚举中发现两个指针可以不回退时,就可以利用这一性质来优化时间复杂度。由于两个指针通常朝着同一个方向移动,因此也常被称为同向双指针。
学习该算法的关键在于理解如何从暴力解法推导至双指针优化,而不是单纯记忆模板。下面通过四个经典案例,带你深入理解其核心逻辑与边界处理。
双指针原理
在滑动窗口模型中,我们维护一个区间 [left, right],通过移动左右指针来调整窗口大小。基本流程如下:
- 进窗口:right 位置元素加入统计(如哈希表);
- 判断:检查当前窗口是否满足条件(如出现重复字符、包含特定种类等);
- 出窗口:若不合法或需收缩,left 右移并移除对应元素;
- 更新结果:记录当前窗口的最优解。
注意:技巧上可以使用 STL 容器或数组模拟哈希效果,具体取决于题目对空间和时间的需求。
经典算法题解析
2.1 唯一的雪花
题目链接:洛谷 UVA11572
思路分析
我们需要找到最长的子串,使得其中没有重复字符。固定起点 left,向右扩展 right。一旦 right 扫描到重复字符,说明当前窗口不合法。此时 right 无需继续向后,因为窗口只会更大更不合法;同时 left 右移一格后,right 也不必回退,因为以 left+1 为起点的最优解不会比 left 差。
代码实现
#include <iostream>
#include <unordered_map>
using namespace std;
const int N = 1e6 + 10;
int a[N];
int main() {
int t;
cin >> t;
while (t--) {
int n;
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
int l = 1, r = 1;
unordered_map<int, int> mp; // 维护窗口内所有元素出现的次数
ret = ;
(r <= n) {
mp[a[r]]++;
(mp[a[r]] > ) {
mp[a[l++]]--;
}
ret = (ret, r - l + );
r++;
}
cout << ret << endl;
}
;
}


