滑动窗口:窗子怎么伸缩

LeetCode 209 · 长度最小的子数组。给定正整数数组和目标 target, 找出和 ≥ target 的最短连续子数组。

拖动改目标值,演示会重新推演。

left
0
right
窗口和
0
当前最短

按「下一步」开始。

0 / 0

代码

def minSubArrayLen(target, nums):
    left = 0
    total = 0
    ans = float('inf')

    for right in range(len(nums)):
        total += nums[right]              # ① 右边先吃进来

        while total >= target:            # ② 满足了就往回缩
            ans = min(ans, right - left + 1)   # ③ 记录必须在缩之前
            total -= nums[left]
            left += 1

    return 0 if ans == float('inf') else ans

三个容易错的地方

① 为什么是 while,不是 if

右指针吃进一个大数之后,窗口可能一次能缩好几格还满足条件。 用 if 只缩一次,就会漏掉更短的答案。 把 target 拖到 3 试试:某些步会连着缩两次,那正是 while 在起作用。

② 为什么答案要在收缩之前

进到 while 里面,说明此刻窗口是满足条件的。 一旦执行了 total -= nums[left],窗口就可能不再满足了—— 这时候再记长度,记的是个不合法的窗口。 求最短的题,一律「满足就记,记完再缩」。

③ 左指针为什么不会跑过右指针

因为数组元素都是正数,窗口缩到只剩一个元素时, total 就是 nums[right] 本身; 再缩一次 total 归零,必然小于 target,循环就退出了。 题目条件里的「正整数」不是废话——有负数这套就不成立,得换前缀和。

求最短 vs 求最长

同样是可变长窗口,这两类的收缩时机正好相反,别记混:

求最短(209)求最长(3)
什么时候缩窗口满足条件时缩窗口违反条件时缩
什么时候记缩之前记缩之后记
循环里的形状while 满足: 记录; 缩while 违反: 缩
记录
为什么合法窗口只在缩之前存在缩完才重新变合法

记法:缩的那一刻,窗口的合法性正在改变。 求最短是「从合法缩到不合法」,所以记录在前; 求最长是「从不合法缩回合法」,所以记录在后。