Python算法训练序列

双指针与滑动窗口 · Unit 07

← Unit 06
209
LeetCode 209Minimum Size Subarray Sum

窗口第一次满足条件,
只是收缩的开始。

寻找最短可行区间时,right负责把窗口和扩张到目标以上;一旦满足条件,就不断移动left,逐步剥离左端元素,并在每次仍然可行时更新更短答案。

LeetCode #209 原题内容中文转述 · 示例、约束与提交签名

题目要求

给定一个由正整数组成的数组 nums 和一个正整数 target,找出总和大于等于 target 的最短连续子数组长度。如果不存在这样的子数组,返回 0。

class Solution: def minSubArrayLen(self, target: int, nums: List[int]) -> int: # 返回满足和不小于target的最短连续长度

约束

  • 1 ≤ target ≤ 10⁹
  • 1 ≤ len(nums) ≤ 100000
  • 1 ≤ nums[i] ≤ 10000

示例

target = 7, nums = [2,3,1,2,4,3]
输出:2
最短子数组为[4,3]
target = 4, nums = [1,4,4]
输出:1
target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0

关键前提

数组元素全部为正数,因此right右移时窗口和不会减小,left右移时窗口和不会增大。这一单调性是滑动窗口成立的基础。

1. 区分最短目标
2. 体验重复枚举
3. 满足后持续收缩
4. 代码联动
5. 乘积窗口迁移
target = 7,nums = [2,3,1,2,4,3]等待操作
current_sum2
当前窗口长度1
历史最短 best∞
当前连续窗口 nums[left:right+1][2]

当前循环不变量

current_sum始终等于nums[left:right+1]的元素和。

当前移动规则

和不足时继续扩张;和达到target后,更新best并持续收缩,直到窗口重新不满足。

left0
right0
当前和2
是否满足否
最佳长度∞
从每个起点重新累加的重复工作

朴素方法从每个left重新向右求和,最坏需要O(n²)。由于元素全为正,旧窗口去掉左端后,其余元素和仍可直接复用,无需重新累计。

补全 LeetCode 提交代码最短可行窗口模板
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
left =
current_sum = 0
best =
for right in range(len(nums)):
while :
best = min(best, )
current_sum -= nums[left]
left += 1
return
class Solution: def minSubArrayLen(self, target: int, nums: List[int]) -> int: left = 0 current_sum = 0 best = len(nums) + 1 for right in range(len(nums)): current_sum += nums[right] while current_sum >= target: best = min(best, right - left + 1) current_sum -= nums[left] left += 1 return 0 if best == len(nums) + 1 else best
为什么必须是while?一个right位置可能对应多个满足条件的窗口。例如加入4后,[2,3,1,2,4]和[3,1,2,4]都满足target;只有持续收缩,才能找到该right下最短的可行窗口。
代码—窗口和—最短值联动轨迹:[2,3,1,2,4,3] / 7
1class Solution:
2 def minSubArrayLen(self, target: int, nums: List[int]) -> int:
3 left = 0
4 current_sum = 0
5 best = len(nums) + 1
6 for right in range(len(nums)):
7 current_sum += nums[right]
9 while current_sum >= target:
10 best = min(best, right - left + 1)
11 current_sum -= nums[left]
12 left += 1
14 return 0 if best == len(nums) + 1 else best
逐行播放,观察窗口第一次达到8后如何连续收缩,最终得到[4,3]长度2。
完成代码联动后进入乘积窗口迁移补全正确、查看答案或播放完整轨迹后解锁。

迁移题:LeetCode #713 乘积小于K的子数组

数组仍由正整数组成。right加入元素并更新乘积;当乘积大于等于k时持续除去左端。窗口合法后,以right结尾的合法子数组共有 right-left+1 个。

本单元形成的算法Skill

面对正数数组中的最短满足型连续区间,right负责扩大状态直到条件成立;条件成立后,必须用while持续移动left,并在每次收缩前更新答案。正数保证窗口和具有单调性,使已经移出的左端无需重新考虑。

LeetCode #209最短窗口正数单调性满足后收缩先更新再移除O(n)
#15锚点与去重
#3最长合法窗口
#438固定频次窗口
#209 当前满足后持续收缩
#76 下一单元频次缺口与最小覆盖