5.双指针算法
主要思想:
- 针对单一序列使用两个指针,如快速排序
- 针对不同的两个序列使用两个指针,如归并排序
双指针算法可以实现对暴力(朴素)算法的优化,使时间复杂度由O(n^2)优化到O(n),要求序列有单调关系
代码模板
for(int i=0,j=0;i<n;i++){
while(j<=i&&check(i,j)) j++;
//具体逻辑
.....
}
例:给定一个由单词组成的字符串,单词之间用空格相隔,实现输出各个单词
实现思路:使用双指针算法,设置指针i,j,指针i指向单词首部,指针j向后移找到单词之间的空格。输出j和i之间的单词,再让i后移到j的位置,j继续后移找到下一个空格,循环直至j移动到末尾。
#include <iostream>
#include <string>
using namespace std;
int main(){
string str;
getline(cin,str);//读入空格
int n=str.length();
for(int i=0;i<n;i++){
int j=i;
while(j<n&&str[j]!=' ') j++;
for(int k=i;k<j;k++) cout<<str[k];
i=j;
}
return 0;
}
AcWing 799.最长连续不重复子序列
实现思路:
- 暴力算法:两重for循环实现所有元素的枚举,选出满足条件的结果
- 双指针算法:设置双指针,指针
j指向结果区间的首元素,指针i指向结果的区间的末尾元素。- 整数序列用数组
a[]存储,设置一个计数数组s[],s[a[i]]表示元素a[i]出现的次数,最后s[]中为1的元素就是最终结果区间中的元素。 j指针首先指向序列首部,i指针不断后移,同时对指向的元素进计数。- 当出现某个元素计数值>1,即出现重复时,
j指针指向的元素计数要减1,且j指针后移直到此时区间不含重复的数,然后记录当前区间长度,i继续后移判断。循环直到i指针移动到末尾。
- 整数序列用数组
#include <iostream>
#include <algorithm>
using namespace std;
const int N=1e6+10;
int a[N],s[N];
int n,res=0;
int main(){
cin>>n;
for(int i=0;i<n;i++) cin>>a[i];
for(int i=0,j=0;i<n;i++){
s[a[i]]++;
while(s[a[i]]>1){
s[a[j]]--;
j++;
}
res=max(res,i-j+1);
}
cout<<res;
return 0;
}
AcWing.800 数组元素的目标和
实现思路:
- 暴力算法,直接两重for循环解决,时间复杂度O(n*m)
- 双指针算法,对于某一个
i有一个j满足a[i]+b[j]>=x,且j最小,即为最终的结果。时间复杂度为O(n+m),因为第二重while循环实际上总共只执行m次(j从m减到0) - 注意:假如不只一个解,则不能使用这个双指针算法
#include <iostream>
using namespace std;
const int N=1e6+10;
int n,m;
int a[N],b[N];
int main(){
int x;
cin>>n>>m>>x;
for(int i=0;i<n;i++) cin>>a[i];
for(int j=0;j<m;j++) cin>>b[i];
//双指针算法
for(int i=0,j=m-1;i<n;i++){
while(j>=0 && a[i]+b[j]>x) j--;
if(a[i]+b[j]==x){
cout>>i>>" ">>j;
break;
}
}
return 0;
}