滑动窗口的核心在于用双指针维护一个满足特定条件的区间,并在移动过程中更新答案。
五步进阶路线,从基础模板到结合高级数据结构的综合应用。
滑动窗口系统学习规划
第零步:前置 Python 技能确认
在开始之前,请确认你能熟练使用以下 Python 特性,它们是写出 Pythonic 滑动窗口的关键:
collections.Counter与defaultdict(int):用于窗口内元素的频率统计,替代手动dict.get(x, 0)。- 海象运算符
:=:在while循环条件中同时赋值和判断,常用于窗口收缩。 enumerate:在遍历右指针时同时获取索引和值。max/min与生成器表达式:用于在窗口内快速求最值(简单场景)。all/any:用于检查窗口内元素是否满足某种条件。
第一步:定长窗口与基础模板 📏
核心思想 :窗口长度固定为 k,右指针逐格移动,左指针跟随。我们关注的是窗口滑入和滑出的元素,以及如何 O(1) 更新窗口状态,而不是每次都重新计算整个窗口。
预期收获:
- 掌握定长滑动窗口的入窗-出窗标准模板。
- 理解"增量式更新"而非"全量重算"的效率差异。
- 学会用 Python 的切片与
Counter简化窗口操作。
学完后自查:
- 能否用
for i, x in enumerate(arr)控制右指针,用i - k定位左指针元素? - 能否用海象运算符
:=在while中收缩窗口(虽然定长不常用收缩,但要会写)? - 能否解释为什么定长窗口通常不需要
while收缩循环?
📋 推荐练习(按顺序做)
| # | 题目 | 难度 | Python 特异性技巧 |
|---|---|---|---|
| 1 | 643. Maximum Average Subarray I | Easy | 定长窗口模板入门,sum 滑动更新,注意浮点除法 |
| 2 | 1343. Number of Sub-arrays of Size K and Average Greater than or Equal to Threshold | Easy | 与上一题同模板,条件判断即可 |
| 3 | 1456. Maximum Number of Vowels in a Substring of Given Length | Medium | 用 set 存元音,窗口更新时判断滑入/滑出元素 |
| 4 | 567. Permutation in String | Medium | 定长视角 :窗口大小固定为 s1 长度,用 Counter 比较两个频率字典是否相等 |
第二步:不定长窗口 --- 最长/最短子数组 🌊
核心思想 :窗口长度不固定,右指针扩展,当窗口不满足条件 时,while 循环收缩左指针直到重新满足。根据求最长 还是最短 ,答案在窗口满足条件时更新 还是收缩后更新。
预期收获:
- 掌握
while收缩与if收缩的语义区别。 - 学会在扩展/收缩过程中正确维护窗口状态(频率、计数、和等)。
- 理解"有效窗口"与"无效窗口"的临界点管理。
学完后自查:
- 能否清楚地描述:求最长时,何时更新答案?求最短时,何时更新答案?
- 能否用
defaultdict(int)或Counter维护频率,并在频率归零时删除键(del或-=0后处理)? - 能否正确使用
while配合:=写出紧凑的收缩逻辑?
📋 推荐练习(按顺序做)
| # | 题目 | 难度 | Python 特异性技巧 |
|---|---|---|---|
| 5 | 209. Minimum Size Subarray Sum | Medium | 不定长窗口入门,求最短,while 收缩模板 |
| 6 | 3. Longest Substring Without Repeating Characters | Medium | 用 set 或 defaultdict 维护无重复,收缩到满足为止,求最长 |
| 7 | 904. Fruit Into Baskets | Medium | 等价于"最多包含 2 种元素的最长子数组",用 Counter 维护种类数 |
| 8 | 76. Minimum Window Substring | Hard | 经典难题 :用两个 Counter(需求 vs 窗口实际),维护 have 和 need 匹配计数 |
第三步:计数型窗口 --- 恰好 K 个与模式抽象 🔢
核心思想 :很多"恰好 K 个"的问题无法直接滑窗(因为窗口扩展时可能从 K 变成 K+1,收缩时又可能从 K 变成 K-1,难以单调维护)。核心技巧是转换为 "最多 K 个"减去"最多 K-1 个",从而复用不定长窗口模板。
预期收获:
- 掌握"恰好" → "最多之差"的转换技巧。
- 学会将"元素种类数 ≤ K"作为窗口合法条件。
- 理解为什么直接求"恰好"往往需要前缀和+哈希,而"最多"可以直接滑窗。
学完后自查:
- 能否在 3 分钟内写出
atMost(K)的模板函数? - 能否解释为什么
exact(K) = atMost(K) - atMost(K-1)? - 能否识别什么样的"恰好"题不适合这个转换(提示:窗口条件非单调时)?
📋 推荐练习(按顺序做)
| # | 题目 | 难度 | Python 特异性技巧 |
|---|---|---|---|
| 9 | 992. Subarrays with K Different Integers | Hard | "恰好 K 种" = atMost(K) - atMost(K-1),Counter 维护种类 |
| 10 | 1358. Number of Substrings Containing All Three Characters | Medium | "至少包含"可以反过来,但更适合用"最少"视角:找到最小窗口后,左指针右侧都可扩展 |
| 11 | 930. Binary Subarrays With Sum | Medium | 和恰好为 goal = atMost(goal) - atMost(goal-1),与上一题同模板 |
| 12 | 1248. Count Number of Nice Subarrays | Medium | 与上题几乎一样,只是把和变成奇数的计数 |
第四步:多元素状态与收缩条件多样化 🧮
核心思想 :不是所有窗口条件都是"频率"或"种类"。有时是 max - min、sum 的上下界、或者需要维护窗口内的极值。这一阶段训练你根据题意设计状态变量的能力,而不再套固定模板。
预期收获:
- 学会维护窗口内多个状态(如最大值、最小值、正/负数计数等)。
- 理解什么时候需要用
heapq或单调队列来维护窗口极值(但完整实现留到第五步)。 - 能够灵活写出非频率类的收缩条件。
学完后自查:
- 能否在一个窗口内同时维护
sum、count_negative、以及某种"前缀信息"? - 能否正确写出
while条件中组合多个逻辑的收缩规则? - 是否理解"收缩到何时停止"对答案正确性的影响?
📋 推荐练习(按顺序做)
| # | 题目 | 难度 | Python 特异性技巧 |
|---|---|---|---|
| 13 | 1004. Max Consecutive Ones III | Medium | 维护窗口内 0 的个数,收缩到 ≤ K,求最长 |
| 14 | 2024. Maximize the Confusion of an Exam | Medium | 与上题同模式,两种字符各自算一遍 |
| 15 | 1438. Longest Continuous Subarray With Absolute Diff Less Than or Equal to Limit | Medium | 需要维护窗口内 max 和 min 的差值,用 heapq 惰性删除或单调队列 |
| 16 | 2401. Longest Nice Subarray | Medium | 窗口内元素两两位与为 0,等价于窗口 OR 和某个新元素与为 0,用位运算维护状态 |
第五步:滑动窗口 + 高级数据结构 ⚡
核心思想 :当窗口收缩条件依赖于窗口内的极值 或顺序信息 ,而直接扫描会退化为 O(nk) 时,需要引入单调队列(collections.deque 实现)或堆(heapq + 惰性删除)来在 O(1) 或 O(log n) 内获取窗口最值。
预期收获:
- 掌握
deque实现单调队列的标准写法(维护单调递减队列取最大值)。 - 掌握
heapq+ 窗口索引做"惰性删除"的技巧。 - 能够分析何时必须引入这些结构,何时简单的变量即可满足。
- 结合前缀和,解决"和 + 最值"类的滑动窗口问题。
学完后自查:
- 能否手写
deque的单调递增/递减队列模板(while deque and nums[deque[-1]] < nums[right]: pop)? - 能否用
heapq配合while heap[0][1] < left: heappop实现惰性删除? - 能否区分"窗口内任意元素"条件和"窗口内最值"条件在实现上的差异?
📋 推荐练习(按顺序做)
| # | 题目 | 难度 | Python 特异性技巧 |
|---|---|---|---|
| 17 | 239. Sliding Window Maximum | Hard | 定长窗口 + 单调队列(deque),经典中的经典 |
| 18 | 862. Shortest Subarray with Sum at Least K | Hard | 前缀和 + 单调队列,窗口收缩条件涉及和与索引 |
| 19 | 480. Sliding Window Median | Hard | 定长窗口 + 对顶堆(两个 heapq)+ 惰性删除,中位数维护 |
| 20 | 1425. Constrained Subsequence Sum | Hard | DP + 单调队列优化,滑动窗口内取 dp 最大值 |
🎯 最终目标:完成这 20 题后,你应该能:
- 看到滑动窗口题,脑海中自动浮现"定长/不定长/最多转恰好/需要单调队列"的分类。
- 用 Python 的
Counter、defaultdict、deque、heapq写出简洁的窗口状态维护。- 在 15 分钟内解决 Medium 级别的滑动窗口题,30 分钟内解决 Hard 级别。