双指针算法(超详细)

文章介绍了如何使用双指针算法解决三个与数组相关的问题:查找最长连续不重复子序列、判断一个序列是否为另一个序列的子序列,以及在两个升序数组中寻找满足特定和的数对。算法利用指针移动控制比较和更新,确保高效解决给定问题。

题目1-最长连续不重复子序列

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

输入格式
第一行包含整数 n。

第二行包含 n 个整数(均在 0∼105 范围内),表示整数序列。

输出格式
共一行,包含一个整数,表示最长的不包含重复的数的连续区间的长度。

数据范围
1≤n≤105
输入样例:
5
1 2 2 3 5
输出样例:
3

//算法思想:用一个长度为100000的数组b来记录每个数字出现的次数,若数据出现就加上1.
//利用双指针,i是先头兵,j在后面收尾,先头兵i每次都往前前进一部,j在后面找到与当前
//的i匹配的最佳的不重复的序列的长度。输出最长的那个。

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>

using namespace std;

const int  N=100000;

int a[N],b[N];

int main(){

    int n;
    cin>>n;

    for(int i=0;i<n;i++) cin>>a[i];

    int res=0,j=0;
    for(int i=0;i<n;i++){
        b[a[i]]++;     //遍历过的a[i]中的元素在b[i]中的计数加1.
        while(b[a[i]]>1){  //如果其中的某个元素出现的次数大于1,就要移动j。
            b[a[j]]--;  //j移动之前要把用来计数的b数组减去一
            j++;        //j向后移动一位
        } 
        res=max(res,i-j+1);
    }

    cout<<res;
    return 0;
}

算法思想:用一个长度为100000的数组b来记录每个数字出现的次数,若数据出现就加上1.
利用双指针,i是先头兵,j在后面收尾,先头兵i每次都往前前进一部,j在后面找到与当前
的i匹配的最佳的不重复的序列的长度。输出最长的那个。

题目2 判断子序列

给定一个长度为 n 的整数序列 a1,a2,…,an 以及一个长度为 m 的整数序列 b1,b2,…,bm。

请你判断 a 序列是否为 b 序列的子序列。

子序列指序列的一部分项按原有次序排列而得的序列,例如序列 {a1,a3,a5} 是序列 {a1,a2,a3,a4,a5} 的一个子序列。

输入格式
第一行包含两个整数 n,m。

第二行包含 n 个整数,表示 a1,a2,…,an。

第三行包含 m 个整数,表示 b1,b2,…,bm。

输出格式
如果 a 序列是 b 序列的子序列,输出一行 Yes。

否则,输出 No。

数据范围
1≤n≤m≤105,
−109≤ai,bi≤109
输入样例:
3 5
1 3 5
1 2 3 4 5
输出样例:
Yes

//算法思想;设置两个指针分别指向两个数组的起始的位置,如果所指向的两个元素相同,则两个指针向前移动一位;
//如果两个元组不同,则只移动b、数组上面的指针。终止条件是如果a上面的指针指向的是最后一个元素的后面,那
//么就输出yes,否则输出no

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>

using namespace std;

const int N=100010;

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 i=0;i<m;i++) cin>>b[i];

    int j=0; int i; //j是指向数组a的指针
    for(i=0;i<m;i++){ //i是指向数组b的指针
        if(a[j]==b[i]) j++;
        if(j==n) break;  //当满足条件时,跳出循环。
    }
    //cout<<i;
    if(j==n) printf("Yes");
    else printf("No");

    return 0;
}

算法思想;设置两个指针分别指向两个数组的起始的位置,如果所指向的两个元素相同,则两个指针向前移动一位;如果两个元组不同,则只移动b、数组上面的指针。终止条件是如果a上面的指针指向的是最后一个元素的后面,那么就输出yes,否则输出no

题目3 数组元素的目标和

给定两个升序排序的有序数组 A 和 B,以及一个目标值 x。

数组下标从 0 开始。

请你求出满足 A[i]+B[j]=x 的数对 (i,j)。

数据保证有唯一解。

输入格式
第一行包含三个整数 n,m,x,分别表示 A 的长度,B 的长度以及目标值 x。

第二行包含 n 个整数,表示数组 A。

第三行包含 m 个整数,表示数组 B。

输出格式
共一行,包含两个整数 i 和 j。

数据范围
数组长度不超过 105。
同一数组内元素各不相同。
1≤数组元素≤109
输入样例:
4 5 6
1 2 4 7
3 4 6 8 9
输出样例:
1 1

//算法思想:由于两个数组都是升序排列,所以可以利用双指针,其中一个指针指向数组A的最右端,另一个指针
//指向数组B的最左端,分别向左和向右移动。当当前指针指向的两个数的和大于目标值时,指向A的指针向左移
//动;当当前指针指向的两个数的和小于目标值时,指向B的指针向右移动;当相等时输出。
//此外,不用考虑有相同的值的情况,因为题目中交代了数据保证有唯一解。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>

using namespace std;

typedef long long ll;

const int N=100000;
ll  a[N],b[N];

int main(){

    int n,m,x;
    cin>>n>>m>>x;

    for(int i=0;i<n;i++) cin>>a[i];
    for(int i=0;i<m;i++) cin>>b[i];

    int j=0,i=n-1; 
    //i指向数组a的最右端,j指向数组b中的最左端。
    while(1){
        ll tmp;
        tmp=a[i]+b[j];
        if(tmp>x) i--;//如果当前两个数的和比x大,i左移
        if(tmp<x) j++;//如果当前两个数的和比x小,j右移
        if(tmp==x) break;
    }

    cout<<i<<" "<<j;

    return 0;
}

算法思想:由于两个数组都是升序排列,所以可以利用双指针,其中一个指针指向数组A的最右端,另一个指针指向数组B的最左端,分别向左和向右移动。当当前指针指向的两个数的和大于目标值时,指向A的指针向左移动;当当前指针指向的两个数的和小于目标值时,指向B的指针向右移动;当相等时输出。此外,不用考虑有相同的值的情况,因为题目中交代了数据保证有唯一解。

### 指针和双指针的概念 #### 指针 在C++中,指针是一个变量,它存储另一个变量的地址。通过指针可以直接访问内存中的数据,这使得操作更加高效[^1]。例如,在函数调用时传递指针而不是整个对象可以减少内存消耗。 ```cpp int a = 10; int* ptr = &a; // ptr 是指向整型变量 'a' 的指针 ``` #### 双指针 双指针是一种算法技巧,通常用于解决涉及数组或链表的问题。该方法使用两个指针遍历数据结构的不同部分,从而提高效率。常见的应用场景包括合两个有序数组、寻找特定条件下的元素组合等[^3]。 ```cpp class Solution { public: void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) { int pos = m-- + n-- - 1; while (m >= 0 && n >= 0) { if (nums1[m] > nums2[n]) { nums1[pos--] = nums1[m--]; } else { nums1[pos--] = nums2[n--]; } } while (n >= 0) { nums1[pos--] = nums2[n--]; } } }; ``` 上述代码展示了如何使用双指针从两个已排序数组中选取较大值填充目标数组[^3]。 --- ### 算法数据结构的应用场景 #### 数据结构 - **堆栈**: 实现撤销功能、表达式求值等问题。 - **队列**: 处理先进先出的任务调度、广度优先搜索(BFS)等。 - **散列表**: 快速查找键值对的数据集合。 - **二叉树**: 表达层次关系、文件系统导航等。 #### 算法 - **线性搜索**: 在无序的小规模数据集中查找指定项。 - **二进制搜索**: 对于大规模有序数据集提供高效的查找方式[^2]。 - **递归**: 解决可分解成更小子问题的情况,比如汉诺塔游戏或者斐波那契序列计算。 - **快速排序(QuickSort)**: 将大数组分割为较小的部分分别处理以达到整体排序的目的。 - **贪心算法(Greedy Algorithm)**: 如活动选择问题,总是挑选最早结束的时间段以便安排更多会议[^4]。 - **动态规划(Dynamic Programming)**: 背包问题是典型例子之一,旨在找到装载物品的最大价值而不超重[^4]。 --- ### C/C++ vs Python 虽然两者都能很好地支持各种高级特性,但在实际开发过程中存在差异: | 特性 | C/C++ | Python | |--------------|--------------------------------|-----------------------------| | 性能 | 更接近硬件层面,运行速度更快 | 抽象程度高,执行较慢 | | 学习曲线 | 较陡峭 | 平缓易上手 | | 内存管理 | 手动分配释放 | 自动垃圾回收 | 对于初学者来说,Python可能是更好的起点因为它语法简洁明了;然而当涉及到高性能需求项目时,则可能倾向于采用C/C++解决方案。 --- 相关问题
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

KKdlg

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

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

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

打赏作者

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

抵扣说明:

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

余额充值