基础算法:双指针算法(最长连续不重复子序列,数组元素的目标和,判断子序列)

背景知识:

双指针:

双指针指的是在遍历对象的过程中,不是普通的使用单个指针进行访问,而是使用两个相同方向快慢指针或者相反方向对撞指针的指针进行扫描,从而达到相应的目的。

一般用法:

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;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值