AcWing算法基础(3)

文章介绍了双指针算法的概念和优势,通过三个具体的例子展示了如何利用双指针解决数组问题,包括寻找最长连续不重复子序列、数组元素的目标和以及判断子序列。此外,还提到了位运算和离散化等技术在处理特定问题时的作用,如处理值域大但数值个数少的情况,并介绍了区间合并的概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、双指针算法

1,算法分析:

        所谓双指针算法,也就是用两个指针去扫描数组,有可能是扫描同一个数组,也有可能是两个不同的数组,凡是用到类似方法都可以称为双指针,双指针算法的优点就在于能够把用暴力解决的所需时间复杂度为 O\left ( N^{2} \right ) 的方法优化成  O\left ( N \right ),大大提高了问题的解决效率。其算法模板如下:

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

    // 具体问题的逻辑
}
//常见的问题:
//  (1) 对于一个数组,用两个指针维护一段区间
//  (2) 对于两个数组,维护某种次序,比如归并排序中合并两个有序序列的操作

2,例题1,799.最长连续不重复子序列

(1)题目描述:

        给定一个长度为n的整数序列,请找出最长的不包含重复的数的连续区间,输出它的长度。

(2)数据范围:

        所有的整数均小于10e5,n小于10e5。

(3)问题样例

输入:
5
1 2 2 3 5
输出:
3
// 3指的是数组中的“235”

(4)问题分析:

        先考虑暴力求解的方法,再思考i和j分别有没有单调关系,再进一步用双指针来优化。

我们用 i 来遍历[i:0→n]数组,用 j 来指向能够在左端且距离当前 i 指向的数字最远的数字,根据分析,我们先说结论:在i往后移的时候,j 绝不可能往前移动,也就是 j 和 i 之间具有单调性。随后再来证明一下这个结论.

 我们来思考一下,当固定 i 不动 i_{0} ,我们何时移动 i ,那必然是 j 已经移动到了此时的''最佳''位置j_{0},当 j 再远离 i 的时候已经指向了一个与此时 i 指向的数字相同的数字了 j_{1},此时我们才需要移动 i 来找下一个数字 i_{1} 的''最佳长度'',那么这个时候新的 i_{1} 的左端必定包含的有 i_{0},如果此时 j 是向后移动,就会使 i,j 之间直接出现重复的数字,故 j 只能向后移动,也就是说 i 往后移动的时候,j 不会往前移动,二者具有单调性。

两个指针的功能定义好了,剩下的就是''check(j,i)''这个判断的问题了,我们要检查的是当前的j和i之间的数字是否有重复,根据问题的数据范围,我们就单开一个数组s[N]记录下每个数字(如果数字很大的话,就可以用C++的哈希表来解决),看代码:

(5)代码

#include<stdio.h>
#define N 100010

int n;
int q[N],s[N];

int max(int x, int y)
{
    return x>y?x:y;
}

int main()
{
    scanf("%d",&n);
    for(int i = 0 ;i < n ; i ++)
        scanf("%d",q+i);
    int ans = 0;
    int j = 0;
    for(int i = 0;i < n ; i ++)
    {
        s[ q[i] ] ++;
        while(j < i && s[ q[i] ] > 1)
        {
            s[ q[j] ]--;
            j++;
        }   
        //☆while循环是为了在遇到两个相同的数字的时候,更新j,使得j指向该数字第一次出现的下一位
        ans=max( ans, i-j+1 );
    }
    printf("%d",ans);
    return 0;
}

其中,while循环这一步请多花时间思考,用样例模拟一下。 

3,例题2,800. 数组元素的目标和

(1)题目描述:

给定两个有序数组A(数组长度n),B(数组长度m),以及一个目标值x,数组下标从0开始, 请求出满足A[i]+B[j]=x,的数对(i,j)题目保证有唯一解。

(2)数据范围:

数组长度n,m不超过10e5,数组元素的值不超过10e9,保证同一个数组无相同元素。

(3)问题样例

输入样例:(第一行分别为n,m,x)
4 5 6
1 2 4 7
3 4 6 8 9
输出样例:
1 1

 (4)问题分析:

先从暴力循环开始考虑,无非两层for循环,组合所有的A[i],B[j],时间复杂度为O(m*n),因为A,B数组分别是严格单调上升序列,所以我们可以利用这一点来优化算法,使用双指针。

注意:这是一个经典的双序列双指针题,经典的操作。

我们不妨用i指针对A数组从前往后扫,用j指针对B数组从后往前扫,A[i]+B[j]≠x的时候无非就两种情况,A[i]+B[j]>x,或者是A[i]+B[j]<x,由于双指针是对着扫的,A[]从最小值开始,B[]从最大值开始,若A[i]+B[j]>x,那么我们要使A[i]+B[j]整体变小,就只能移动j指针,反之,若A[i]+B[j]<x,只能移动i指针。可以发现利用双指针,我们就可以把O(m*n)的时间复杂度降低成线性的O(m+n)。

(5)代码

#include<stdio.h>

#define N 100010

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

int main ()
{
    scanf("%d%d%d", &n, &m, &x);
    for(int i = 0 ; i < n ; i++) scanf("%d",a+i);
    for(int j = 0 ; j < m ; j++) scanf("%d",b+j);
    
    int j= m - 1 ;
    for(int i = 0 ; i < n ;i ++)
    {
        while(a[i]+b[j]>x) j--;
        if(a[i]+b[j]==x) printf("%d %d",i,j);
    }
    return 0;
}

 4,例题3,2816. 判断子序列

(1)题目描述:

给定长度分别为n,m的序列,a,b,请你判断 a 序列是否为 b 序列的子序列。子序列指序列的一部分项按原有次序排列而得的序列,如\left \{ a_{1},a_{3},a_{5} \right \} 是序列 \left \{ a_{1},a_{2},a_{3},a_{4},a_{5} \right \} 的子序列。

(2)数据范围:

序列长度均不大于10e5,序列元素均不大于10e9

(3)问题样例:

输入样例:
3 5
1 3 5
1 2 3 4 5
输出样例:
Yes

 (4)问题分析:

这个题一画图就可以找到i和j的单调关系,根据子序列的定义,如果我们找得到所有a序列中的元素,且把这些元素在b数组中下标组成一个新的数组的话,这个新的数组就是一个递增数组,通过进一步的分析,我们知道当指向序列a的指针向后移动的时候,要想满足子序列定义,指向序列b的指针不可能向前移动,只能向后移动,所以i,j之间有着单调性。

我们只需要遍历b数组(遍历a数组理论上是可行,但是我写代码卡住了),当满足条件a[i]==b[j]的时候就让i++,最后只需要判断一下i是否等于n(或者从1开始n+1)即可。

(5)代码

#include<stdio.h>
#define N 100010

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

int main()
{
    scanf("%d%d",&n,&m);
    for(int i = 1 ; i <= n ; i ++) scanf("%d",a+i);
    for(int j = 1 ; j <= m ; j ++) scanf("%d",b+j);
    int i = 1 ;
    for(int j =1 ; j <= m ; j++)
    {
        if(i<=n && a[i] == b[j]) i++;
    }
    if(i==n+1)puts("Yes");
    else puts("No");
    return 0;
}

二、位运算

三、离散化

用于处理值域很大,但是这个值域内的数个数很少,零零散散的几个点,而有些题我们需要用这些点的下标来做,但是又不能开一个匹配值域的数组,比如:

把值与值很分散的数据保序的映射到下标为0和自然数递增序列这个过程就叫做离散化

那我们需要解决的问题有两点

  1. a[]中可能有重复的元素,如何去重,排序(为了降低后续二分的时间复杂度)
  2. 如何计算出a[]离散化后的值钱---二分

把数组的每一个值映射到他的下标,下标就是他映射的值 ,实现离散化的本质就是二分的方法去找到想要离散化的数据在该数组中的下标位置

四、区间合并

区间合并的功能就是将若干个有交集的区间合并成一个这些区间并集的新区间,也就是包含了这些区间的新区间。

### 回答1: acwing算法基础课是一门针对算法学习的在线课程,在这门课程中,学生可以系统地学习和掌握算法基础知识,提高编程水平。为了方便学生学习,acwing提供了网盘服务。 acwing算法基础课网盘是一个用于存储课程资源的平台。通过这个网盘,学生可以下载课程讲义、代码模板以及补充材料等。这些资源都经过精心整理,供学生们参考和学习。 网盘中的资源是按照课程章节进行分类的,学生可以根据自己的学习需要,选择性地下载所需的资料。同时,网盘还提供了搜索功能,方便学生快速定位和获取所需资料。 acwing算法基础课网盘的使用对于学生们的学习非常有帮助。通过下载和学习这些资源,学生们可以更好地理解课程内容,加深对算法的理解。此外,学生们还可以通过研究代码模板,学习优秀的编程思想和技巧,提高自己的编程能力。 总之,acwing算法基础课网盘是一项非常便利和实用的服务,为学生们提供了更加全面和深入的学习资源,帮助他们更好地掌握和运用算法知识。 ### 回答2: acwing算法基础课是一门优质的算法学习资源,其中的课程内容丰富多样,涵盖了算法基础知识、数据结构、动态规划、图论等等。很多学习者都认为这门课程对他们的算法学习有很大的帮助。 网盘是指以网络为媒介,提供文件存储和下载服务的云存储平台。acwing算法基础课也提供了网盘服务,方便学习者下载课程资料并进行学习。 通过acwing算法基础课网盘,学习者可以方便地获取到课程的各种学习资料,包括讲义、习题集、代码示例等。这些资料可以帮助学习者更好地理解和掌握课程的内容。此外,网盘还提供了上传和分享功能,学习者可以将自己的学习心得、代码等资料分享给其他学习者,促进学习者之间的互相学习和交流。 acwing算法基础课网盘的优点仅仅是方便快捷的下载和分享功能,还包括安全可靠的存储环境。学习者可以放心地将自己的学习资料上传到网盘进行备份,减少据丢失的风险。同时,网盘还提供了多种存储空间容量的选择,满足学习者同的需求。 总的来说,acwing算法基础课网盘为学习者提供了方便、安全和多样化的学习资源下载和分享服务,为学习者的算法学习和进步提供了有力的支持。如果你对算法感兴趣,我推荐你去尝试一下这门精彩的课程!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

RefrainC.C.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值