D
AI
学习工作台
算法2026-05-222 分钟阅读

双指针与滑动窗口

对撞指针、快慢指针与可变窗口求最优/计数

双指针滑动窗口子数组面试记笔记标记疑惑

对撞指针(相向)

适用:有序数组求两数之和、盛水、回文、三数之和(固定一端 + 双指针)。

def two_sum_sorted(nums, target):
    i, j = 0, len(nums) - 1
    while i < j:
        s = nums[i] + nums[j]
        if s == target:
            return [i, j]
        elif s < target:
            i += 1
        else:
            j -= 1
    return []

排序后 sum < target 只能增大左端;sum > target 只能减小右端,每步排除一种组合,总 O(n)

三数之和:外层枚举 i,内层对 [i+1, n-1] 用对撞指针,注意跳过重复元素。

快慢指针(同向)

适用:链表环、找中点、删除倒数第 k、原地去重、数组划分。

  • 环检测:快指针每次走 2 步,慢走 1 步,相遇则有环(Floyd)。
  • 划分slow 维护有效区,fast 扫描,如「移除元素」「移动零」。
def remove_duplicates(nums):
    if not nums:
        return 0
    slow = 1
    for fast in range(1, len(nums)):
        if nums[fast] != nums[fast - 1]:
            nums[slow] = nums[fast]
            slow += 1
    return slow

滑动窗口(子串 / 子数组)

维护 [left, right] 区间,用哈希或数组记录窗口内字符/计数,在单调条件下移动。

最长无重复子串

def length_of_longest_substring(s):
    last = {}
    left = ans = 0
    for right, ch in enumerate(s):
        if ch in last and last[ch] >= left:
            left = last[ch] + 1
        last[ch] = right
        ans = max(ans, right - left + 1)
    return ans

可变窗口求最小长度(和 >= target):右扩累加和,满足时不断左缩并更新最小长度。

def min_subarray_len(target, nums):
    left = total = 0
    ans = float("inf")
    for right, x in enumerate(nums):
        total += x
        while total >= target:
            ans = min(ans, right - left + 1)
            total -= nums[left]
            left += 1
    return 0 if ans == float("inf") else ans

面试归纳

| 信号 | 套路 | |---|---| | 有序 + 配对 | 对撞 | | 子串/连续子数组 + 约束 | 滑动窗口 | | 链表环/中点 | 快慢 | | 原地 O(1) 空间 | 同向双指针 |

写代码时明确:不变量(窗口内满足什么)、何时移动 left答案在扩张还是收缩时更新。LeetCode 3、76、209、15、11 是高频起点。

知识卡片

问题

双指针能降低复杂度的前提是什么?

点击翻转查看答案

答案

移动指针时存在单调性:例如排序后和指针只增不减、或窗口扩大/收缩时某指标单调,从而将 O(n²) 降为 O(n)。

问题

固定长度窗口与可变长度窗口如何区分?

点击翻转查看答案

答案

固定 k:右指针每次右移,左指针同步右移保持长度 k;可变:右扩直到满足条件,再左缩直到不满足,期间更新答案。

问题

滑动窗口求「最长满足条件子串」时窗口大小趋势?

点击翻转查看答案

答案

先右扩满足条件并记录;再左缩直到不满足,重复;答案在满足条件的扩张阶段取 max。