前言
双指针算法在实际场景中非常实用,对于某些看似需要O(n^2)
的时间复杂度才可以解决的问题,我们可以利用题目场景对应的某种特殊的单调性
对算法进行优化,让时间复杂度巧妙地降到O(n)
。
for (int i = 0, j = 0; i < n; i ++ ) {
while (j < i && check(i, j)) j ++ ;
// 具体问题的逻辑
}
// 常见问题分类:
// (1) 对于一个序列,用两个指针维护一段区间
// (2) 对于两个序列,维护某种次序,比如归并排序中合并两个有序序列的操作
一、题目陈述
二、解决思路
如果采用朴素的做法解决,肯定有O(n^2)
的复杂度,显然速度太慢了。通过分析我们可以发现,在用j指针
从前向后枚举区间结束位置的时候,i指针
代表的对应区间能到达的最左的开始位置,要么不动,要么会向右移动,不可能向左回撤。(可以用反证法证明。)
三、代码实现
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1e5+10;
// 原数组
int a[N];
// 哈希数组
int hx[N];
int main() {
int n;
cin>>n;
for(int i=0;i<n;i++) cin>>a[i];
int res = 0;
for(int i=0,j=0;i<n;i++) {
hx[a[i]] ++;
// check从j到i有没有重复元素
while(hx[a[i]]>1 && j<=i) {
hx[a[j]] --;
j++;
}
res = max(res, i-j+1);
}
cout<<res<<endl;
return 0;
}
总结
双指针算法是对暴力枚举O(n^2)
的优化,优化依据是题目环境下可以证明的单调性。这道题中,i指针和j指针都可以移动一遍数组的全部位置,即2*n
,所以时间复杂度被降到了O(n)
。