双指针算法

对于两重for循环

for(int i = 0; i < n; ++i)
    for(int j = 0; j < n; ++j)
    {
        //具体代码
    }//O(n^2)

双指针算法的思想就是:寻找指针i 与 j 之间是否有单调的关系。若存在,则利用此关系来把整个枚举的状态数量从O(n^2)降到O(n),模板如下

for(int i = 0, j = 0; i < n; ++i)
{
    while(j < i && check(j, i)) ++j;//此行为降低时间复杂度的关键。check()函数反映指针i与j的性质

    //每道题目的具体逻辑
}

最典型的应用如下:

给你一个英文字符串,找到每一个单词并逐行输出

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

using namespace std;
/**
 * 把一个字符串中的所有单词找到,并按行输出
 * */
int main()
{
    char str[100];
    gets(str);
    int n = strlen(str);
    for(int i = 0; i < n; ++i) cout << str[i];
    cout << '
';
    
    for(int i = 0; i < n; ++i)
    {
        int j = 0;
        while(j < i && str[j] != ' ') ++j;
        
        //双指针算法的具体逻辑部分
        for(int k = i; k < j; ++k)
            printf("%c", str[k]);
        cout << '
';
    }
}

当然了,往往会卡在这个j与i指针之间的关系并不好找
最长连续不重复子序列
给定一个长度为n的整数序列,请找出最长的不包含重复数字的连续区间,输出它的长度。

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

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

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

数据范围
1≤n≤100000


具体解题思路:运用桶排思想,用数组s[]来记录每个输入的数字a[i]出现的次数,当s[a[i]]出现的次数大于1时,[j, i]区间构成的子序列就必定有重复元素,因此我们只能通过右移j指针,来试图在[j, i]区间中找到这个重复的元素并将其排除(s[a[i]]--; ++j;)每次j指针右移我们都需要检查一下s[a[i]]是否已经等于1(数字a[i]是否仅在新的区间[j, i]中出现一次)来判断是否停止移动j指针

具体代码如下

#include <iostream>
#include <string>

using namespace std;

const int N = 100010;
int n;
int a[N], s[N];//桶排序思想,当数据范围过大时应该用哈希表来求解

int main()
{
    ios::sync_with_stdio(false);
    cin >> n;
    for(int i = 0; i < n; ++i) cin >> a[i];

    int len = 0;//记录最长连续子序列
    for(int i = 0, j = 0; i < n; ++i)
    {
        s[a[i]]++;
        while(/*j <= i&& 没必要写*/s[a[i]] > 1)//当s[a[i]]出现的次数大于1时,[j, i]区间构成的子序列就必定有重复元素
        {
            s[a[j]]--;
            j++;
        }
        len = max(len, i - j + 1);
    }

    cout << len << endl;
}

声明:acWing课程学习总结
作者:DriftingAE86

原文地址:https://www.cnblogs.com/theSunAndSnow/p/12254389.html