滑动窗口题解:窗口移动靠条件,不靠感觉

📅 2026/7/6 5:19:12 👁️ 阅读次数 📝 编程学习
滑动窗口题解:窗口移动靠条件,不靠感觉

滑动窗口题解:窗口移动靠条件,不靠感觉

一、滑动窗口不是双指针套皮

滑动窗口常用于子数组、子串、连续区间问题。很多人看到连续就上左右指针,但写着写着就乱:什么时候右移,什么时候左移,窗口内维护什么,答案何时更新,全靠感觉。真正的滑动窗口依赖明确条件。

窗口移动不是模板背诵,而是维护一个可验证的不变量。

二、先定义窗口语义

flowchart TD A[右指针扩张] --> B[加入新元素] B --> C{窗口是否合法} C -->|否| D[左指针收缩] C -->|是| E[更新答案] D --> C

窗口通常表示[left, right][left, right)。区间开闭要先定清楚,不然后面边界会乱。

sliding_window_steps: define_window: true expand_right: true shrink_until_valid: true update_answer_at_correct_time: true

每一步都要知道自己在维护什么条件。

三、例子:最长无重复子串

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

这里窗口不变量是:s[left:right+1]内没有重复字符。右指针加入字符后,如果重复位置在窗口内,就把 left 移到重复字符后一位。

注意seen[ch] >= left。如果重复字符已经在窗口外,就不该移动 left。这是很多边界错误的来源。

四、不同题型更新答案位置不同

求最长合法窗口,通常在窗口合法后更新答案;求最短满足条件窗口,通常在满足条件时收缩并更新答案;求恰好 K 个条件,有时要转化成“最多 K 减最多 K-1”。

def at_most_k(nums, k): left = 0 ans = 0 count = {} for right, x in enumerate(nums): count[x] = count.get(x, 0) + 1 while len(count) > k: y = nums[left] count[y] -= 1 if count[y] == 0: del count[y] left += 1 ans += right - left + 1 return ans

这个函数统计最多 K 种不同元素的子数组数量。每次窗口合法后,新增的合法子数组数量是right - left + 1

滑动窗口的难点不是代码长,而是答案含义。更新 max、min、count 的位置不同,背模板会很容易错。

最后,做题时可以先写暴力解,明确“枚举哪个区间”,再把重复枚举变成窗口维护。这样比直接套模板更稳。

五、总结

滑动窗口题解要先定义窗口语义和合法条件,再决定扩张、收缩和答案更新位置。

窗口移动靠条件,不靠感觉。只要不变量站住,代码就不会一改边界就崩。