双指针算法介绍:
双指针算法是一种常用的算法技巧,它通常用于在数组或字符串中进行快速查找、匹配、排序或移动操作。
双指针并非真的用指针实现,一般用两个变量来表示下标(在后面都用指针来表示)
双指针算法使用两个指针在数据结构上进行迭代,并根据问题的要求移动这些指针。双指针往往也和单调性、排序联系在一起,在数组的区间问题上,暴力法的时间复杂度往往是O(n^2)的,但双指针利用“单调性”可以优化到O(n)。
双指针一般又分为对撞指针和快慢指针;两个都比较重要,所以我们一个一个详细介绍:
<1>,对撞指针:
1,对撞指针介绍
指的是两个指针 left、righ(简写为l和r)分别指向序列第一个元素和最后一个元素,然后l指针不断递增,r不断递减,直到两个指针的值相撞或错开(即1>=r),或者满足其他要求的特殊条件为止。
对撞指针一般用来解决有序数组或者字符串问题(常见于区间问题):
查找有序数组中满足某些约束条件的一组元素问题:比如二分查找、数字之和等问题
字符串反转问题:反转字符串、回文数、颠倒二进制等问题。
2,求解步骤
1.使用两个指针 left,right。left 指向序列第一个元素,即:left=1,right 指向序列最后一个元素,即:right=n。
2.在循环体中将左右指针相向移动,当满足一定条件时,将左指针右移,left++。当满足另外一定条件时,将右指针左移,right--。
3.直到两指针相撞(即left==right),或者满足其他要求的特殊条件时,跳出循环体。
下面开始举例:
eg1:lanqiao OJ 回文判定
题目分析:
联想回文数字的特点,就是首尾相同;
//双指针
//撞指针
//回文 lanqiao OJ 1371
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+9;
char s[N];
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>s+1;//重点注意这里,char类型,cin>>s+1,相当于从第一位输入而不是第0位;
int len=strlen(s+1);
int l=1,r=len;
bool ans=true;
while(l<r&&ans)
{
if(s[l]!=s[r])
ans=false;
l++;
r--;
}
cout<<(ans?'Y':'N');
return 0;
}
对撞指针比较简单,就是两个伪指针相向移动,下面我们重点学习快慢指针;
<2>,快慢指针
1,快慢指针介绍
快慢指针一般比对撞指针更难想,也便难写;
指的是两个指针从同开始遍历序列,且移动的步长一个快一个慢;
移动快的指针被称为快指针,移动慢的指针被称为慢指针。为了方便理解,我们称快指针为r,慢指针为l,这样慢指针和快指针构成区间[l,r]。
两个指针以不同速度、不同策略移动,直到快指针移动到数组尾端,或者两指针相交,或满足其他特殊条件时为止。
2,解题步骤:
1.使用两个指针 l、r。l一般指向序列第一个元素,即:l=1,r一般指向序列第零个元素,即:r=0。即初始时区间[1,r]= [1,0]表示为空区间。
2.在循环体中将左右指针向右移动。当满足一定条件时,将慢指针右移,即l++。当满足另外一定条件时(也可能不需要满足条件),将快指针右移,即r++,保持[l,r]为合法区间。
3.到指针移动到数组尾端(即l==n且r==n),或者两指针相交,或者满足其他特殊条件时跳出循环体。
举个栗子:
eg2:lanqiao OJ 1372 美丽的区间
刚开始接触快慢指针有可能有一点不太熟练,但是会了之后还是很简单的
//快慢指针
//双指针--lanqiao OJ 1372//美丽的区间
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+9;
int a[N],s;
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int n;
cin>>n>>s;
for(int i=1;i<=n;i++)
cin>>a[i];
int ans=n+1;
for(int i=1,r=0,sum=0;i<=n;i++)//这里的i就相当于l
{
while(i>r||r+1<=n&&sum<s)
sum+=a[++r];//注意
if(sum>=s)
{
ans=min(ans,r-i+1);
sum-=a[i];
}
}
//要注意这里,如果ans>n,说明这个数组总和加起来也没有s大;;;
cout<<(ans>n?0:ans)<<endl;
return 0;
}
eg3:lanqiao OJ 1621 挑选字串
这道题和上一题几乎一样,只不过换了一种问法,建议做完上一题后自己做一下这一题
上代码:
//快慢指针
//双指针--lanqiao OJ 1621 挑选字串,和上一题差不多
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+9;
int a[N];
int main()
{
int n,s,k;
cin>>n>>s>>k;
for(int i=1;i<=n;i++)
cin>>a[i];
int ans=0;
for(int i=1,r=0,sum=0;i<=n;i++)//这里的i就相当于l
{
while(i>r||((r+1)<=n)&&sum<k)
sum+=(a[++r]>=s);//注意,这里用了一个逻辑表达式;
if(sum>=k)
{
ans+=n-r+1;
sum-=(a[i]>=s);
}
}
cout<<ans<<endl;
return 0;
}