类型
双指针算法主要可分两种:
- 两数组双指针(代表:归并排序)
- 单数组双指针(代表:快速排序)
复杂度
O(n): 确切说是2n。
伪代码模板
for(int i=1;i<=n;i++){
int j=i;
while(j<=n&&[j]满足某周性质){
j++;
}
接下来是针对不同题的的不同逻辑
}
或
for(int i=1,j=0;i<=n;i++){
while(j<=i&&[j]满足某周性质){
j++;
}
接下来是针对不同题的的不同逻辑
}
例题
ACwing799
题目大意:
给一个n长的递增数列,求最长不重复且连续的子序列长度。
解法一:
双指针的第一指针指向起始位置,第二指针指向新要判断的位置,再用一个map记录每个数字从i开始第一次出现的位置并且进行判重。
AC代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>
#define in(x) scanf("%d",&x)
#define debug(x) cerr<<#x<<" : "<<x<<endl;
using namespace std;
const int maxn=100005;
int a[maxn];
map<int ,int >v;
int n;
int main(){
in(n);
for(int i=1;i<=n;i++){
in(a[i]);
}
int ans=0;
for(int i=1;i<=n;i++){
int j=i;
while(j<=n&&!v[a[j]]){
v[a[j]]=j;
j++;
}
ans=max(ans,j-i);
if(j>=n){
break;
}
i=v[a[j]];
v.clear();
}
cout<<ans<<endl;
return 0;
}
需注意的是每次while循环结束后要判断j是否走到结尾。(不然i会一直更新且无法退出循环)
解法二(ACwing Y老师的方法)
第一个指针i指向新加入的元素,然后让其出现次数s[a[i]]++,如果其次数已经大于1次,则利用第二个指针(记录起始位置)去找重复的位置,并且更新起始位置(同是要把其他不在所求区间内的元素出现次数归零)。
#include <iostream>
#include <cstdio>
#include <cstring>
#define in(x) scanf("%d",&x)
#define debug(x) cerr<<#x<<" : "<<x<<endl;
using namespace std;
const int maxn=100005;
int a[maxn],s[maxn];
int n;
int main(){
in(n);
for(int i=1;i<=n;i++){
in(a[i]);
}
int ans=0;
for(int i=1,j=0;i<=n;i++){
s[a[i]]++;
while(s[a[i]]>1){
s[a[j]]--;
j++;
}
ans=max(ans,i-j+1);
}
cout<<ans<<endl;
return 0;
}
ACwing800:
题目大意
给出两个有序序列,和一个目标整数x
现有两个数字来自不同的序列,求可以使其相加为x的下标。
解题思路
第一个指针按照线性遍历整个数组,第二个指针按照二分查找。
AC代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int maxn=100005;
int a[maxn],b[maxn];
int main(){
int n,m,x;
cin>>n>>m>>x;
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
}
for(int i=0;i<m;i++){
scanf("%d",&b[i]);
}
int i=0,j=0;
for(i=0;i<n;i++){
int tar=x-a[i];
int l=0 ,r=m-1;
int mid=l+r+1>>1;
while(l<r){
mid=l+r+1>>1;
if(b[mid]<=tar){
l=mid;
}
else {
r=mid-1;
}
}
if(b[l]!=tar){
continue;
}
cout<< i<<" "<<l<<endl;
break;
}
return 0;
}
ACwing2816:
题目大意:
有两个序列,判断第一个序列是否为第二个序列的子序列(不连续)。
解题思路:
第一个指针指向要判断在不在子序列里的数字
第二个指针指向要遍历的长序列,没找到继续找,找到了跳出循环,第一个指针向后挪动。
AC代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#define in(x) scanf("%d",&x)
#define debug(x) cerr<<#x<<" : "<<x<<endl
using namespace std;
const int N=100005;
int a[N],b[N];
int main(){
int n,m;
in(n);in(m);
for(int i=1;i<=n;i++){
in(a[i]);
}
for(int i=1;i<=m;i++){
in(b[i]);
}
int i=1,j=1;
int tag=1;
for(;i<=n;i++){
int tar=a[i];
while(j<=m&&a[i]!=b[j]){
j++;
}
if(j>m){
tag=0;
cout<<"No\n";
break;
}
j++;
}
if(tag) cout<<"Yes\n";
return 0;
}