双指针算法

1.基本介绍
严格的来说,双指针只能说是是算法中的一种技巧。

双指针指的是在遍历对象的过程中,不是普通的使用单个指针进行访问,而是使用两个相同方向(快慢指针)或者相反方向(对撞指针)的指针进行扫描,从而达到相应的目的。最常见的双指针算法有两种:一种是,在一个序列里边,用两个指针维护一段区间;另一种是,在两个序列里边,一个指针指向其中一个序列,另外一个指针指向另外一个序列,来维护某种次序。
在这里插入图片描述
2.模板

for (int i = 0, j = 0; i < n; i ++ )  // j从某一位置开始,不一定是0
{
    while (j < i && check(i, j)) j ++ ;

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

双指针算法的核心思想(作用):优化
在利用双指针算法解题时,考虑原问题如何用暴力算法解出,观察是否可构成单调性,若可以,就可采用双指针算法对暴力算法进行优化.
当我们采用朴素的方法即暴力枚举每一种可能的情况,时间复杂度为O(n*n)。

    for(int i = 0; i < n; i++){
        for(int j = 0; j < n; j++){
            //具体逻辑
        }
    }

而当我们使用双指针算法时通过某种性质就可以将上述O(n*n)的操作优化到O(n)。

3.例题
例题01、

先看这样一个例子:输入一个字符每个子串之间有一个空格,让你输出每一个空格后的子串。

输入
abc def hij
输出

abc
def
hij

参考代码:

#include<iostream>
#include<string>

using namespace std;

int main()
{
    string str;
    getline(cin, str);
    int n = str.size();
    
    for(int i = 0; i < n; i++)
    {
        int j = i;
        
        while(j < n && str[j] != ' ') j++;
        
        // cout<<j;
        for(int k = i; k < j; k++) cout<<str[k];
        cout<<endl;
        
        i = j; //循环体执行完后for()中的i才 i++即,下一次开始时 i就到了上一次空格(位置j)的下一位 
    }
    return 0;
}

在这里插入图片描述
题目:最长连续不重复子序列
在这里插入图片描述
思路:
i指针从头开始依次遍历每一个数组单元
j指针表示离i最远的左侧位置
开始时i和j都在开头
在i指针遍历数组单元的同时并且用另一个数组记录下每一个数组单元所出现的次数
如果某个数组单元出现的次数大于1,那么一定是目前i指针所指的位置
因为i指针所指的位置是新记录的数组单元,在i之前没有出现过重复,那么一旦重复出现必在i位置。

#include <iostream>
using namespace std;
const int N = 100010;

int n;
int s[N],a[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(j <=i && s[a[i]] > 1) {
            s[a[j]]--;
            j++;
        }
        res = max(res, i-j+1);
    }
    cout<< res <<endl;
    return 0;
}

题目:3
数组元素的目标和
在这里插入图片描述
思路:
为了减少循环,采用了两个指针来保证能遍历符合要求的所有数;
i指针从A数组的头开始,此时a[i]最小;
j指针从B数组的尾开始,此时b[j]最大;
在i = 0 时,判断a[i] + b[j] 的值,如果大于x,j- -;
也就是说b[j]的值一直在减小;
那么当a[i] + b[j] 的值第一次小于x时,j指针的左边的数与a[i]相加一定都小于x了;那么我们只能增大a[i]的值;
于是i ++;
自此开始反复循环,如果a[i] + b[j] 大于x,就减小b[j](相当于将j指针左移)反之,就增大a[i](相当于i指针右移)
这样,一定保证了每一个符合要求的两个数都能加一遍,判断是不是和等于x。

#include <iostream>

using namespace std;

const int N = 100010;
int n,m,x;
int a[N], 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 (a[i] + b[j] == x) {
            cout << i << " "<<j <<endl;
            break;
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值