2020/10/5 Acwing-双指针算法

题目1

给定一个长度为n的整数序列,请找出最长的不包含重复的数的连续区间,输出它的长度。
输入格式
第一行包含整数n。
第二行包含n个整数(均在0~100000范围内),表示整数序列。
输出格式
共一行,包含一个整数,表示最长的不包含重复的数的连续区间的长度。
数据范围
1≤n≤100000
输入样例:
5
1 2 2 3 5
输出样例:
3

题目分析

题目要求的是对输入的序列,找到那些满足:不包含重复的数的子序列,并记下来这些序列的长度,输出最长序列的长度.关键是如何遍历到所有的满足条件的子序列,

算法分析

本题使用的算法是双指针算法,双指针算法是简化版的朴素算法(暴力),朴素算法是直接两个嵌套循环从而遍历所有子序列,但是时间复杂度是O(n^2),双指针算法的出现是在找到规律的前提下,减少遍历的复杂度(也就是减少不必要的遍历).
现在分析题目:

  1. 首先建一个指针i,这是不论朴素还是双指针算法都必需的,将此指针作为另一个指针遍历的终点(也就是说i,j作为子序列的边界)
  2. 现在先让指针i遍历,当指针i确定在某一个指针,那么另一个指针需要每次都从0开始遍历吗? 这里就是规律所在了,另一个指针其实只能向右走,而不能向左走,也就是说j的遍历完完全全不需要一个for循环来从头遍历,而只需要接着之前的位置走即可.*那么是为什么呢?*这里我们首先假设ij已经确定了自己的位置,即j的位置是以i为结尾的满足条件的最远位置.下一次遍历,i指针向右走,j指针可以前移吗?当然不能,因为前移表示的是此时的ij是满足条件的最大至值,而此区间其实包扩了 之前的i+前移的j,也就是说,前一个的选取出现了问题,这就出现了矛盾.
    在这里插入图片描述
    因此,j这个指针只能向右前进,而不需要从头遍历

其他需要考虑的问题

算法解决的是如何加快遍历的速度,但是如何确认i,j的位置,使得ij间没有重复的数字也没有解决.
这里使用的是一个数组S[],数组里存储的是当前区间内各个数字出现的次数,角标表示的是数字. 例如s[4]=2表示数字4在区间内出现了2次.
同时我们要知道,i的遍历是一定的,也就是说我们一直在寻找的其实是j的最佳位置.因此,其实区间的变化包含两部分:终点i向右移动1格,在此前提下起点j向右找满足条件的点.所以我们看到,其实数组区间先是增加了1个数q[i],如果重复一定是新增加的这个数重复,(当然没有重复是最好的,j不动区间是最大的),所以我们要做的是:

  1. i向右移动1个位置,计数区间对应数的次数+1
  2. 判断此时这个数的个数有没有大于1(大于1说明有两个此数)
  3. 如果大于1,则j要向右移动,同时j所表示的数的个数-1,接着进行2的操作/如果没有大于1,说明没有重复,此时i的最大区间已经确定,就是i-j+1.
    //形象的比喻其实就是一个排队的通道,只能站不一样高的人,这时一个vip用户(比如175cm)直接站到队的最前面,如果大家都不一样高,那当然没问题,可如果不满足条件,就需要有人从队尾离开,队伍一直从后向前离开,直到队伍里除了vip的最后一个175离开才算结束.

源代码

import java.util.Scanner;
class Main{
    public static void main(String[] args){
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        int[] q=new int[100100];
        int[] s=new int[100100];
        int res=0;
        for(int i=0;i<n;i++){
            q[i]=sc.nextInt();
        }
        for(int i=0,j=0;i<n;i++){
            s[q[i]]++;
            while(j<i&&s[q[i]]>1){//最后j会停在使i的数只有1个的位置
                s[q[j]]--;
                j++;
            }
            res=Math.max(res,i-j+1);
        }
        System.out.print(res);
    }
    
}

题目2

给定两个升序排序的有序数组A和B,以及一个目标值x。数组下标从0开始。
请你求出满足A[i] + B[j] = x的数对(i, j)。
数据保证有唯一解。
输入格式
第一行包含三个整数n,m,x,分别表示A的长度,B的长度以及目标值x。
第二行包含n个整数,表示数组A。
第三行包含m个整数,表示数组B。
输出格式
共一行,包含两个整数 i 和 j。
数据范围
数组长度不超过100000。
同一数组内元素各不相同。
1≤数组元素≤109
输入样例:
4 5 6
1 2 4 7
3 4 6 8 9
输出样例:
1 1

题目说明

给出两个有序且无重数组,并给出一个和,目的是找到两个数组中哪两个数的和等于给出的和,找到他们,返回他们的角标.

算法解析

同样是使用双指针算法.如果使用朴素算法,就是在第一个数组遍历,再嵌套一个第二个数组遍历.然而对于有序的数组,没有利用到这个条件就会浪费时间.
所以对于双指针算法的思维方式应该是这样:

  1. 首先无论是朴素还是双指针,必须有一个指针i,就叫他慢指针,慢指针从0开始,到数组未结束
  2. 再寻找的时候,另一个快指针的选择从数组尾部开始,关键在于,j指针一定是只向左走,因为i指针是一直向右走,如果前一个i配合j都比x大,那么i向右走一定更比x大了,所以j只需要向左走即可

源代码

import java.util.Scanner;
class Main{
    public static void main(String[] args){
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        int m=sc.nextInt();
        int x=sc.nextInt();
        int N=100100;
        int[] a=new int[N];
        int[] b=new int[N];
        for(int i=0;i<n;i++){
            a[i]=sc.nextInt();
        }
        for(int i=0;i<m;i++){
            b[i]=sc.nextInt();
        }
        int i,j;
        for(i=0,j=m-1;i<n;i++){
            while(a[i]+b[j]>x&&j>0){
                j--;
            }
            if(b[j]+a[i]==x)    {
        break;
        }
        }
        System.out.print(i+" "+j);
    }
}

双指针模板

for(int i=初始化,j=初始化;i<n;i++){
while(关于j的表达式的判断){
j进行移动}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值