背景知识:
双指针:
双指针指的是在遍历对象的过程中,不是普通的使用单个指针进行访问,而是使用两个相同方向(快慢指针)或者相反方向(对撞指针)的指针进行扫描,从而达到相应的目的。
一般用法:
1、在一个序列中一个指针从首端向后移动,另一个指针从尾端向前移动。
2、两个指针都从首端向后移动,但是移动速度不一样,一个移动的快,一个移动的慢(在这里是指 快指针在前“探路”,当符合某种条件时慢指针再向前移动)。
【模板】:
2.例题:
1.最长连续不重复子序列:
1.可以先想暴力的做法:对每个 i 和 j 都遍历一遍,对每个 i 和 j 都check一下中间的数据是否满足给定的条件。这样的时间复杂度是O(n^2);数据稍微大点就会超时。
【模板】:
修正:上图中的res应该等于max(res,i - j + 1)。
2:双指针方法:
思路:
使用两个指针i ,j 从数列初位置开始分别向右走,i 走到数组末,j走到 i 的位置。 j[ ]是在保证无重复数的情况下距离 i 最远能往左走多少
定义数组 a[ ] , s[ ] 。a[ ]指原数列,s[ ]用来动态地记录当前区间里面每个数出现多少次
for(int i=0,j=0;i<n;i++){
s[a[i]]++;
}
i 每次往后移动一位,对于每个 i 分别求出往左走能到最远地方的 j,再每一次进行check(每一次的j到i之间的序列是否有重复数)。如果有重复数就让j往后移动一格,直到没有重复数为止
while(s[a[i]]>1){
s[a[j]]--;
j++;
}
最后对于所有的 i 求一下最大值就是答案
res=max(res,i-j+1);
【示例】:
输入样例:
5
1 2 3 2 5
输出样例:
3
。
【代码实现】:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int a[N],s[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++){
s[a[i]]++;
while(s[a[i]]>1){
s[a[j]]--;
j++;
}
res=max(res,i-j+1);
}
cout<<res<<endl;
return 0;
}
2.数组元素的目标和:
1.与先前思路一样先想一下暴力的做法:
即用 i循环第一个数组,j循环第二个数组。当以i,j为下标的值相加等于 x时输出i,j就行了
时间复杂度为O(nm)。
2.双指针算法:
用两个指针i,j。利用单调性,则对于每个a[ ]+b[ ]>=x都有单调递增。所以为了求 j的最小值(即让i+j==x),j 应该往j - 1的方向走直到 i+j==x。
【代码实现】:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int a[N],b[N];
int main(){
int n,m,x;
scanf("%d%d%d",&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]);
for(int i=0,j=m-1;i<n;i++){
while(a[i]+b[j]>x)j--;
if(a[i]+b[j]==x)printf("%d %d\n",i,j);
}
return 0;
}
3.,判断子序列:
背景知识:子序列指序列的一部分项按原有次序排列而得的序列,例如序列 {a1,a3,a5}{a1,a3,a5} 是序列 {a1,a2,a3,a4,a5}{a1,a2,a3,a4,a5} 的一个子序列。
题目分析:可以理解为第一个数组里面的数是否能顺次匹配到第二个数组中。
当遍历完第二个数组后,发现第一个数组里面的每一个数都能在第二个数组中顺次匹配,
则第一个序列就是第二个序列的子序列。
【简图】:
【代码实现】:
#include<bits/stdc++.h>
using namespace std;
const int N=1000010;
int a[N],b[N];
int main(){
int n,m;
cin>>n>>m;
for(int i=0;i<n;i++)cin>>a[i];
for(int j=0;j<m;j++)cin>>b[j];
int i=0,j=0;
while(i<n && j<m){
if(a[i]==b[j])i++;
j++;
}
if(i==n)puts("Yes");
else puts("No");
return 0;
}