209. 长度最小的子数组

中等 含交互动画

学完这道你应该能: 说清它为什么用「滑动窗口」,而不是死记步骤; 合上页面,自己默写出核心代码; 用 30 秒把思路讲清楚。

AI 私教 · 就着本题动画讲
卡住了不用硬扛,按 8 步把这题真正走通。
从读题、试解、找法到手写代码,边问边打勾,适合第一次系统学算法的同学。

题目描述

正整数数组中,求和 ≥ target最短连续子数组长度(没有返回 0)。

nums = [2, 3, 1, 2, 4, 3]
target = 7
输出 = 2 (子数组 [4, 3],和 7 ≥ 7)

思路解析

枚举每个起点终点再求和,n² 甚至 n³,还重复加了很多次。换个思路:用一个窗口,右边扩进新数、和够了就从左边缩,每个数只被加一次、减一次,全程只扫一遍。

r 不断右移把数加进窗口;和不够 target 时只能继续扩,一旦窗口和 ≥ target,就记录长度、并尽量把 l 右移缩短窗口(同时减去左端值),直到和不够再继续扩。最短的达标窗口就在「每次刚好够」的瞬间出现。

r=0 · 和不够:负例分支:窗口只有 [2],和 2 还远不到 7,没法缩、只能继续右扩。和不够时左指针绝不动。

r=1 · 还是不够:吃进 3,窗口 [2,3] 和 5,仍不够 7。继续右扩,l 不动。

r=2 · 仍不够:再吃进 1,和 6,就差一点点,还是不够。再扩。

r=3 · 够了!:吃进 2,和 8 第一次 ≥ 7!记下长度 4,现在该缩左看能不能更短。

r=3 · 缩左 → 不够停:把左端 2 移出,和降到 6 小于 7,缩不动了,停。当前最短仍是 4,回去继续右扩。

r=4 · 又够了:r 扩到 4,吃进 4,窗口 [3,1,2,4] 和 10 ≥ 7。又能缩左了。

r=4 · 缩到长度 3:连缩:去掉 3 后 [1,2,4]=7 仍达标,刷新最短为 3;再去掉 1 后 [2,4]=6 不够,停在 l=3。最短已是 3。

r=5 · 最后一次扩:r 扩到最后,吃进 3,窗口 [2,4,3] 和 9 ≥ 7,继续缩左找更短。

r=5 · 缩到最短 [4,3]:去掉左端 2 后窗口 [4,3]=7 ≥ 7,长度 2!这是全程最短,就是答案。

「连续子数组 + 满足某条件求最长/最短」就用滑动窗口:右扩纳入、违规/达标就动左边。无重复最长子串、最小覆盖子串都是它。

参考代码(Python)

Python
1l, s, best = 0, 0, float(class="cl-str">"inf")
2for r in range(len(nums)):
3    s += nums[r]                      # 右边扩进
4    while s >= target:                # 够了就缩左
5        best = min(best, r - l + 1)   # 记录当前长度
6        s -= nums[l]; l += 1          # 缩左,同步减和
7return 0 if best == float(class="cl-str">"inf") else best

复杂度分析

  • 时间复杂度:O(n) —— l 只增不减、r 只走一遍,两根指针合计移动不超过 2n
  • 空间复杂度:O(1) —— 只用 l、s、best 几个变量

套路模板

记住骨架:r 右扩纳入、while 里左缩、缩前/后更新答案。求最短在「达标」时更新、求最长在「违规」前更新,是两类窗口的区别。

Python
1# 「连续子数组/子串 + 求最长/最短」都套
2l = 0
3for r in range(n):
4    加入(nums[r])                     # 右扩
5    while 窗口违规 or 已达标:          # 该缩了
6        更新答案()
7        移出(nums[l]); l += 1         # 左缩

易错点

  • 错误写法:缩窗口时只 l += 1,忘了 s -= nums[l]正确写法:先 s -= nums[l] 再 l += 1(窗口和必须和窗口同步维护;不减左端值,s 会一直偏大、while 永远成立,把窗口缩成空甚至越界)
  • 错误写法:没找到也返回 best(inf)正确写法:最后判 inf 返回 0(题目要求无解时返回 0,直接返回 inf 是错的)
  • 错误写法:收缩用 if 只缩一次正确写法:用 while 一直缩到不达标(一次右扩后可能要连缩好几格(如 r=4 时连缩 3 和 1))

以上文字与上方动画完全同一思路:先看动画建立直觉 → 读文字巩固 → 关掉页面自己默写一遍代码,能写出来才算真的掌握

下一题 →76. 最小覆盖子串 ← 返回题库