双指针算法

双指针算法

文章目录

  • 双指针算法
    • 一、前言
    • 二、基本介绍
    • 三、题型
      • [3.1 单序列](#3.1 单序列)
        • [3.1.1 同向双指针](#3.1.1 同向双指针)
        • [3.1.2 相向双指针](#3.1.2 相向双指针)
      • [3.2 双序列](#3.2 双序列)
      • [3.3 三指针](#3.3 三指针)
        • [3.3.1 思路](#3.3.1 思路)
      • [3.4 分组循环](#3.4 分组循环)
        • [3.4.1 核心思想](#3.4.1 核心思想)
        • [3.4.2 优点](#3.4.2 优点)
        • [3.4.3 适用场景](#3.4.3 适用场景)
        • [3.4.4 思路](#3.4.4 思路)
        • [3.4.5 例题](#3.4.5 例题)
    • 四、小结

一、前言

今天,我们来重温算法题型之滑动窗口问题~在大题中算是一种优化的策略

二、基本介绍

滑动窗口/n指针/尺取法,本质都是一样的。

利用双/n指针遍历获取满足条件的区间的算法

三、题型

3.1 单序列

3.1.1 同向双指针

思路

枚举r,不断得到合法区间(符合条件,但不一定是答案)。记(l, r]为一个序列内以r为终点的合法区间,然后枚举l,随着l的增大,区间不断缩小,直到不合法为止(真正筛选,得到答案)。

总结:滑动窗口相当于在维护一个队列 。右指针的移动可以视作入队 ,左指针的移动可以视作出队

哈希表结合:

cpp 复制代码
// 定长滑窗
// 入
cnt[nums[i]]++;
// 更新(根据题目条件来)
// 出
if(--cnt[out] == 0)
{
    cnt.erase(out);
}
// 不定长滑窗
// 入
cnt[c]++;
// 更新 + 出(根据题目条件来)
while(cnt[c] > 2)
{
    cnt[s[left]]--;
    left++;
}

时间复杂度 : O ( n ) O(n) O(n)

用法 :求取有一定限制的区间个数最短的区间(与区间相关)

优点

  • 不会去枚举到一定不满足条件的区间
  • 不会去枚举符合条件但是一定不是答案的区间

不足:有些情况不可行(双指针的情况有很多种)

满足条件区间和大小满足区间长度单调变化,即:区间越长,区间和越小/大

模板

cpp 复制代码
int n=nums.size();
int l=0,r=0;
int sum=0;
while(r<n)
{
    sum+=nums[r];
    while(l<=r&&sum-nums[l]>=target)
    {
        sum-=nums[l];
        l++;
    }
    if(sum>=target)
    {
        res=min(res,r-l+1);
    }
    r++;
}
  • 初始化左右指针,答案和辅助变量

  • 枚举r(移动右指针),寻找符合条件的,枚举r到超出条件时,就该枚举l了

  • 枚举l来寻找答案

例题

leetcode

  • LCR 008.长度最小的子数组(不定长:对于条件更注意,没有明确的出)
  • 713.乘积小于 K 的子数组(子区间问题 :下标从0开始,短:子区间个数:ans += r - l + 1;长:ans += l
  • 2379.得到 K 个黑块的最少涂色次数(定长:入-更新-出)
3.1.2 相向双指针

思路

两个指针left = 0; right = n − 1,从数组的两端开始,向中间移动,这叫相向双指针 。上面的滑动窗口相当于同向双指针

模板

cpp 复制代码
int i = 0, j = s.size() - 1;
while (i < j) {
    if (!isalnum(s[i])) {
        i++;
    } else if (!isalnum(s[j])) {
        j--;
    } else if (tolower(s[i]) == tolower(s[j])) {
        i++;
        j--;
    } else {
        return false;
    }
}
return true;

终止条件一般为:i < j

适用题型

  • 有序(和大小相关)
  • 回文(和首尾检验有关)

例题

  • 125.验证回文串

3.2 双序列

遍历时兼顾两个序列,和单序列差异不大,甚至更简单

3.3 三指针

3.3.1 思路

一个指针枚举位置,i = 0j = i + 1k = arr.size() - 1

3.4 分组循环

3.4.1 核心思想

外层循环负责遍历组之前的准备工作(记录开始位置),和遍历组之后的统计工作(更新答案最大值)。

内层循环负责遍历组,找出这一组最远在哪结束。

3.4.2 优点

各个逻辑块分工明确,也不需要特判最后一组(易错点)。

3.4.3 适用场景

按照题目要求,数组会被分割成若干组,每一组的判断/处理逻辑是相同的。

3.4.4 思路
cpp 复制代码
// 定义变量
// 一般而言,只需一个变量i充当指针作用即可
while(i < n)
{
    // 第一个while负责寻找各组的起点
    int start = i;
    while(i < n && ...)
    {
        // 第二个while寻找各组的终点
    }
    ans = ...;	// 更新答案
}
3.4.5 例题

2760.最长奇偶子数组

四、小结

本篇结合灵神的题单总结,很多大佬的总结以及自己的感悟而来。。。

相关推荐
2501_901147832 小时前
题解:有效的正方形
算法·面试·职场和发展·求职招聘
你撅嘴真丑2 小时前
习题与总结
算法
亲爱的非洲野猪2 小时前
动态规划进阶:状态机DP深度解析
算法·动态规划
dragoooon342 小时前
[hot100 NO.91~95]
算法
windows_62 小时前
【无标题】
算法
踢足球09293 小时前
寒假打卡:2026-01-24
数据结构·算法
亲爱的非洲野猪3 小时前
动态规划进阶:多维DP深度解析
算法·动态规划
AlenTech3 小时前
197. 上升的温度 - 力扣(LeetCode)
算法·leetcode·职场和发展
橘颂TA4 小时前
【Linux 网络】TCP 拥塞控制与异常处理:从原理到实践的深度剖析
linux·运维·网络·tcp/ip·算法·职场和发展·结构与算法