2024-01-27 16:42阅读: 5评论: 1推荐: 0

(坚持每天写算法)算法复习与学习part1基础算法part1-12——双指针算法

  双指针是一种思路,很多题都可能用得到,这里我就只选取Acwing网站的三道题(事实上我最近就是在这里刷题,leetcode反而不怎么去了,刷完这个网站的我就会去leetcode刷了)

  双指针一般来讲会在数组有序的情况下应用,但是如果是无序的也是有可能的,两个指针会遍历整个数组(如果条件允许的话)。

  我觉得双指针有两种情况,我不知道别人是怎么分类的,我感觉我做过几种题目,一种是两个索引(指针的意思就是索引,并不一定要是数据类型的那个指针),一个在前一个在后,一个往后走一个往前走,根据条件移动直到两个指针相遇,这一种的两个指针目的就是遍历完整个数组。另一种是两个索引,但是两个都是在前面,一个先走另一个根据条件追上去,两指针相遇或者一个到了数组结尾就结束,这一种的数组可以是无序的,并且这一种的主要目的很有可能就是两个指针之间的区间。其他的就是多个数组形成了多指针这种类似的变种了(有新情况我就更新一下)

  那么直接上题目。

1                                           .

   思路:观察题目,题目一开始并没有说是有序的数组,那么直接当成无序的数组来看。题目的意思是找到不重复的数的连续区间,也就是说,我们在找的过程中我们的区间长度都是变动的,数组两端中间的数值不允许重复。其实从这里就可以确定是双指针了,因为双指针就是动态变化的,根据我分的第二类来看,另一个指针就是根据条件来看要不要移动,而这一个条件其实就是某一个数是否重复了。对于某一个数是否重复了,我们可以新建一个数组u,如果重复了那么设定成true,当然这一种我一般是在递归树题目才会使用,双指针的话可以新建一个频率数组,至于怎么用,看代码就可以理解了。

  这里是图解,感谢大佬画的图解:

   这里是代码:

复制代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
//按照示例来做 , 要选择的区间是[i,j],i从0开始循环到n,j从0开始,j不一定循环到n
//而是循环到重复的数的最后一个数(2,2,3,2,j指到第三个2就行),一旦j移动,那么
//区间的s也要删掉,如果不删掉的话while检查条件无法成立,而且题目要求是连续的序列
//j之后不会被赋值为0

//双循环改良,使用while来替代后循环,去掉一些不符合最终条件的数

//关于一些双指针,Bug-Tree大佬有笔记

//for(int i = 0 ; j = 0 ; i < n ; i ++){
//     while(j < i && check(i , j)) j ++;
// }


const int N = 100010;
int n;
int a[N] , s[N];

int main(){
    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);//i - j + 1 是因为左闭右闭(建议回想下前缀和的区间),j
        //
    }

    cout << res;

    return 0;

}
复制代码

   我们观察一下这个代码,其实双指针是有模板的(嗯,我觉得有就是有),我理解的模板:1.for循环,遍历数组。2.while过滤,while是核心代码。3.if条件,有时候有有时候没有,用来减少无意义代码或者是别的用途,按照题目来讲。我在第一道题的代码的注释里面也有提到在这个模板

  我们来看第二道题目

2. 

                                     

  思路:很容易看出来是遍历数组来找到符合条件的,因为有多个数组,所以很容易想到双指针(哈哈),这一种做出来肯定很容易,难的是时间限制了吧,我们必须尽可能想到如何过滤掉一些没用的,嗯,就是if条件了,回想一下双指针的模板,以下是代码:

  代码:

复制代码
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>

using namespace std;

const int N = 100010;
//观察了一下,这道题用双指针改良,其实就是我想的那样,在里面添加条件就可以了,我看了下题解,就是从后面往前,j还是要经过整个数组
//其实不太符合我内心对双指针的想法。
//
//总结:判断条件是否要进去操作(以减少操作数据规模)和while过滤是双指针的核心
//双指针和双层循环,使用双层循环时候(如果该代码是核心代码的话)可以考虑可不可以使用双指针删掉可能存在的一些无意义
int n , m , x;
int a[N];
int b[N];

int main(){
    cin >> n>>m>>x;
    for(int i = 0 ; i < n ; i ++)cin >> a[i];
    for(int i = 0 ; i < m ; i ++)cin >> b[i];
    
    for(int i = 0 , j = m - 1 ; i < n ; i++){
        while(j >= 0 && a[i] + b[j] > x) j--;
        if(j >= 0 && a[i] + b[j] == x) cout << i << " " << j << endl;
    }
    
    return 0;
}
复制代码

  也是for+while+if。那么直接来看第三道题目吧

  第三道题目

3.

   思路:这一道题目明显就是要按照有序的数组来看,因为子序列的要求就是这样子的(),这一道题的思路就是两个数组两个指针,A数组的指针是遍历用的,按顺序遍历到A数组的尾部的过程中如果有和B数组一样的元素那么B数组的指标就会移动,如果A数组遍历完了B数组指针还没到尾部那么B数组就不是A数组的序列。因为是双指针我们直接看模板就可以了。

  这里是代码:

复制代码
//过早总结了,还有一题,同样需要过滤的while和操作的if条件
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>

using namespace std;
const int N = 100010;

int n,m;
int a[N];
int b[N];

int main(){
    cin >>n>>m;
    for(int i = 0 ; i < n ; i ++) cin >> a[i];
    for(int i = 0 ; i < m ; i ++) cin >> b[i];
    int t = n;//t是a数组还没有被匹配的数量

    for(int i = 0 , j = 0 ; i < n; i ++){
        while(a[i] != b[j] && j < m ) j ++; //while进行过滤
        if(j >= m) break;//过滤的时候发现j已经到头了那么就直接break吧,这一步容易没有考虑到
         //匹配状态,其实暗藏着一个if条件a[i] == b[j] ,这是我们操作的条件
            t --; 
            j ++;//匹配到了那么就下一个
    }
    //数量为0则全部匹配成功,不为0就说明b数组按照a数组匹配还没有匹配完b数组就没了,那么结束
    if(t > 0) cout << "No";
    else cout << "Yes";

    return 0;
}
复制代码

 

本文作者:程序计算机人

本文链接:https://www.cnblogs.com/clina/p/17991506

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   程序计算机人  阅读(5)  评论(1编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起
  1. 1 Lions Pride Audio artist
  2. 2 Salty Sailor David Arkenstone
  3. 3 Stonefire David Arkenstone
Salty Sailor - David Arkenstone
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.

Not available