Acwing第三课之双指针


提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档


一、双指针算法

双指针算法,顾名思义,即使用两个指针的算法,如之前所学的快排、归并都是利用了双指针,双指针可以在一个序列中使用,也可以在两个序列中使用,可以把朴素算法O(n^2)的时间复杂度读降低为O(n)。
比如使用两个指针 i 和 j时,对 i 进行循环,保证 j 不会回头,只朝着一个方向移动。i 和 j 可以朝同一个方向扫描,也可以反方向扫描。有时候两个指针也可以用不同的速度前进,比如双指针判断链表成环,一个指针速度为1,另一个指针速度为2。
朴素算法思路模板

for(int i = 0 ,i < n ; i++){		//这里直接暴力思路
  for(int j = 0, j < n; j++){
  }
}

双指针算法思路模板

for(int i = 0 ,i < n ; i++){		
  while( j < i && check(i, j)) j++;		//这里的核心是j不会每次都从头开始了
}														//check函数就是判断对于目前的 i 和 j,j是否需要继续右移 

1.最长连续不重复子序列问题

在这里插入图片描述
分析题目,这里需要两个指针,分别用来确定子序列的左边界、和右边界。这里的核心是让 j 只从前到后走一遍, i 指针为右边界,j指针为左边界。这里利用了一个辅助数组b[ ],用来存放数a[ i ]在目前的 [j,i]区间有几个数。b[a[ i ]] > 1 即为新加入的i和之前的数有重复了,通过右移 j , 保证[j,i]区间不重复。每次记录的res都是对于当前的 i ,寻找最小的 j,j 再小就会右重复数字了。

import java.util.Scanner;
public class Main{
    public static void main(String[] args){
        Scanner s = new Scanner(System.in);
        int n = s.nextInt();
        int res = 0;    //用来存放结果
        int[] a = new int[n];
        int[] b = new int[100010];
        for(int i = 0;i < n;i++){   //初始化数组
            a[i] = s.nextInt();
        }
        for(int i = 0,j = 0;i < n;i++){
            b[a[i]]++;
            while(j < i && b[a[i]] > 1) {   //s[a[i] > 1,即为i+1造成的重复还没有消除,j++
                b[a[j]]--;                  //序列中去除a[j]
                j++; 
            }                       //循环结束后,即为一个不含重复数字的子序列,判断是否变大
        res = res > (i - j + 1) ? res:(i - j + 1);      //更新序列长度
        }
      
        System.out.println(res);
    }
    
}

2.数组元素的目标和

在这里插入图片描述

代码如下(示例):
这题还是用双指针的方法。下面两段代码,第一部分,对于暴力方法优化了一点,但是每次 i 更新 j 还是会从0开始移动,只不过移动的范围变小了。

import java.util.Scanner;
public class Main{
    public static void main(String[] args){
        Scanner s = new Scanner(System.in);
        int n = s.nextInt();
        int m = s.nextInt();
        int x = s.nextInt();
        int c = m;
        int i=0,j=0;
        int[] a = new int[n];
        int[] b = new int[m];
        for(i = 0; i < n;i++)
            a[i] = s.nextInt();
        for(i = 0; i < m;i++)
            b[i] = s.nextInt();
        for(i = 0;i<n; i++){
            j = 0;
            while(a[i] + b[j] < x && j < c-1)       //这里必须是c-1,如果最后一个依然满足又多加了
                j++;
            // if(j==c)
            //     j--;
            if(a[i]+b[j]==x)
                break;
            else
                c = j+1;
        }
        System.out.println(i+" "+j);
    }

这里数据一大还是超时了,关键在于不应该让指针j再回滚了,双指针的目标就是O(n)一遍走过。
需要进行优化,想办法让 j 不回头的只移动一遍,分析两个数组的特性。让其分别从两端开始移动,a数组从左边移动,最小的开始,b的 j 从右边开始。如果a[i]+b[j] > x, 则 j 向左移动,因为最小的 i 也不能满足其相加等于 x。可以直接删除这个数了,反之则右移 i ,直到找到正确的i和j最多O(n)即可完成查找。

import java.util.Scanner;
public class Main{
    public static void main(String[] args){
        Scanner s = new Scanner(System.in);
        int n = s.nextInt();
        int m = s.nextInt();
        int x = s.nextInt();
        int i=0,j=0;
        int[] a = new int[n];
        int[] b = new int[m];
        
        for(i = 0; i < n;i++)
            a[i] = s.nextInt();
        for(i = 0; i < m;i++)
            b[i] = s.nextInt();
       

        i = 0 ;
        j = m-1;
        while(true){
            if(a[i] + b[j] < x)
                i++;
            else if(a[i] + b[j] > x)
                j--;
            else 
                break;
        }
        System.out.println(i+" "+j);
    }
}

2.数组元素的目标和

在这里插入图片描述
这个题目也很简单,两个序列对应两个指针 i 和 j 。直接套用模板即可。

import java.util.Scanner;
public class Main{
    public static void main(String[] args){
        Scanner s = new Scanner(System.in);
        int n = s.nextInt();
        int m = s.nextInt();
        int[] a = new int[n];
        int[] b = new int[m];
        for(int p = 0;p < n;p++)
            a[p] = s.nextInt();
        for(int p = 0;p < m;p++)
            b[p] = s.nextInt();
        int t =  0;
        boolean f = false;
        int i = 0,j = 0;
        for(i = 0, j = 0;i < n;i++){
            while(j < m && a[i]!=b[j]) j++;
            if(j < m && a[i] == b[j]){
                t++;
                j++;
            }
            if(j==m)
                break;
                
        }
        if(t == n)
            System.out.println("Yes");
        else
            System.out.println("No");
        
        
    }
}

总结

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值