双指针算法

文章目录

  • 思路简介
  • 代码模版
  • 使用时机
  • 时间复杂度
  • 例题
    • CODE
    • 思路解析



思路简介

大致有两种情况

  • 对于两个序列,我们需要对里面的某些值进行操作
  • 更多的时候是对一个序列来说,我们需要对前后两个值进行操作

这些时候,我们就需要用两个指针去寻找满足条件的两个值


代码模版

for(int i = 0, j =0; i < n; ++i){
	...			// 一些操作
	while(j <= i && check(j)){
		...		// 一些操作
		j++;
	}
}

整体代码思路就是(以同一个序列来说):

  1. 两个指针i, j从头开始遍历
  2. i作为前指针往前走,然后每走一步按照题目要求进行操作
  3. 每次操作完检测是否满足条件,满足则i继续下一轮循环
  4. 如果不满足,j指针开始行动,每走一步检测是否符合check()函数条件(也就是题目要求),直到满足,j停下

使用时机

什么时候用双指针?
如果我们需要区间内找两个数时,我们会用两个指针,当这两个指针单调时(就是两个指针所指的值具有某些关联性时),我们就可以用双指针代替两重循环的暴力枚举了,将时间复杂度由 O ( n 2 ) O(n^2) O(n2) 降到 O ( n ) O(n) O(n)


时间复杂度

虽然我们看起来是两重循环枚举,但是由于j指针跟i指针有关联性,所以i往前遍历时,j指针不会回退,所以整个循环遍历下来总共遍历了 2 n 2n 2n 次,时间复杂度也就是 O ( n ) O(n) O(n)


例题

板子题:https://www.acwing.com/activity/content/problem/content/833/

CODE

#include 
#include 
#include 

using namespace std;

const int N = 1e5 + 10;
int a[N], s[N];
int n;

int main()
{
    cin >> n;
    for(int i = 0; i < n; ++i) scanf("%d", &a[i]);
    
    int res = 0;
    for(int i = 0, j = 0; i < n; ++i){
        s[a[i]] += 1;
        while(s[a[i]] > 1){
            s[a[j]]--;
            j++;
        }
        res = max(res, i - j + 1);
    }
    
    cout << res << endl;
}

思路解析

既然找不重复的子序列,那么就是一个区间,那么我们双指针寻找区间左右端点即可。
我们用一个数组记录每个数出现过的次数,每当发现超过 1 1 1 就说明该序列出现重复了,而且就在i指针指向的位置。

这段代码最妙的点在于j指针的检测条件,直接用标记数组的值来判断,我一开始还想着用个指针遍历一下[i, j]区间呢 <_> 太菜了。


你可能感兴趣的:(算法学习记录,算法,c++,笔记)