1.双指针定义
双指针时候又叫做尺取法或者滑动窗口,我们一般定义两个指针一个左指针left, 一个右指针right当条件满足时根据左右区间更新结果
有时候我们会发现不一定需要套两层for循环才能得出结果,如果使用双指针的话且指针不用回退那我们就可以使用双指针法,将我们时间复杂度由O(n ^ 2)优化为了O(n)

2.双指针经典例题
2.1唯一的雪花 Unique Snowflakes
这道题是我们双指针的模板题,我们可以首先创建一个哈希表用来保持这个元素出现的次数,当我们的右指针遍历到时,让这个元素进窗口也就是利用哈希表记录这个元素出现的次数加一,当这个区间不合法时(出现重复元素)让左指针向右指针的方向移动,左指针移动时会让左指针移动前指向的那个元素在这个区间出现的次数减一,当窗口变成重新合法时我们就更新结果,对这些结果取一个最大值即可:
cpp
#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 left = 1, right = 1, ret = 0;
unordered_map<int, int> mp;
while (right <= n)
{
//记录次数
mp[a[right]]++;
//判断并回到合法窗口
while (mp[a[right]] > 1)
{
mp[a[left]]--;
left++;
}
ret = max(ret, right - left + 1);
right++;
}
cout << ret << endl;
}
}
2.2逛画展
其实用双指针来解决的题模板都差不多,主要是体现在进窗口和出窗口已经更新方式的不同,其余的地方还是比较的相似的
这道题的目标是在尽可能小的区间内看过所有的画,所以我们可以定义一个变量cnt用于记录这个区间内有几个画家,很明显这道题是允许区间内有重复的元素的,所以我们假设哈希表为mp,这个值为x的话,当mp[x]冲零变一时就是区间内有新画家的画,让cnt++,当cnt满足时更新一下结果当mp[x]从一变零时代表区间内少了一位画家的画,最后我们取最小的区间大小就可以了:
cpp
#include <iostream>
#include <unordered_map>
using namespace std;
const int N = 1e6 + 10;
int a[N];
int n, m;
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> a[i];
unordered_map<int, int> mp;
int left = 1, right = 1;
int cnt = 0, begin = 1;
int ret = n;
while (right <= n)
{
if (mp[a[right]] == 0) cnt++;
mp[a[right]]++;
while (cnt == m)
{
int len = right - left + 1;
if (len < ret)
{
ret = len;
begin = left;
}
if (mp[a[left]]-- == 1) cnt--;
left++;
}
right++;
}
cout << begin << ' ' << begin + ret - 1 << endl;
return 0;
}
2.3字符串
这道题和上一题几乎一摸一样就是换了一个条件而已了,所以这里就直接贴代码了
cpp
#include <iostream>
#include <unordered_map>
using namespace std;
const int N = 1e6 + 10;
string s1;
unordered_map<char, int> mp;
int king;
int main()
{
cin >> s1;
int n = s1.size();
int left = 0, right = 0, ret = 0x3f3f3f3f;
while (right < n)
{
if (mp[s1[right]]++ == 0) king++;
while (king == 26)
{
ret = min(ret, right - left + 1);
if(mp[s1[left]]-- == 1) king--;
left++;
}
right++;
}
cout << ret << endl;
return 0;
}
2.4丢手绢
根据题目的条件我们可以这道小朋友之间的距离是小于总距离和sum / 2的,所以当两个小朋友的距离不合法时出窗口并更新结果在结果里取最大值即可
cpp
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
typedef long long LL;
int n;
LL a[N];
int main()
{
LL sum = 0; cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
sum += a[i];
}
int left = 1, right = 1;
LL k = 0;
LL ret = 0;
while (right <= n)
{
k += a[right];
while (2 * k >= sum)
{
ret = max(ret, sum - k);
k -= a[left++];
}
ret = max(ret, k);
right++;
}
cout << ret << endl;
return 0;
}
完