这道题不是在考暴力,也不是在考前缀和,
而是在考你会不会“动态维护一个合法区间”。
一、题目回顾
给定一个含有 n 个 正整数 的数组 nums,和一个正整数 target。
请找出该数组中 满足其和 ≥ target 的长度最小的连续子数组,并返回其长度。
如果不存在符合条件的子数组,返回 0。
二、暴力解法为什么不行?
最直接的思路是:
- 枚举所有子数组
- 计算它们的和
- 找满足条件的最短长度
但这样做:
- 子数组数量:O(n²)
- 计算和:O(n)
👉 总复杂度 O(n³) ,直接超时。
即使优化成前缀和,也有:
- 枚举区间:O(n²)
👉 还是不够好。
三、问题的关键信息(决定解法)
请注意题目中的一个 极其重要的隐藏条件:
数组中的元素全部是正整数
这意味着:
- 右指针右移 → 区间和只会变大
- 左指针右移 → 区间和只会变小
👉 区间具有“单调性”
👉 非常适合:滑动窗口 + 双指针
四、核心思想:动态维护一个“合法窗口”
一句话理解
用一个窗口包住一段连续区间,
右边扩张让它合法,左边收缩让它最短。
五、滑动窗口整体思路
我们维护四个变量:
left:窗口左边界right:窗口右边界sumWindow:当前窗口内元素之和minLen:满足条件的最小长度
算法流程
-
right不断向右移动,扩大窗口 -
累加窗口和
sumWindow -
当
sumWindow >= target:- 说明当前窗口合法
- 尝试不断移动
left,压缩窗口
-
在压缩过程中更新最小长度
六、代码实现(滑动窗口标准模板)
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int n = nums.length;
int left = 0;
int minLen = n + 1;
int sumWindow = 0;
for (int right = 0; right < n; right++) {
sumWindow += nums[right];
while (sumWindow >= target) {
minLen = Math.min(minLen, right - left + 1);
sumWindow -= nums[left];
left++;
}
}
return minLen == n + 1 ? 0 : minLen;
}
}
七、为什么这里一定要用 while,而不是 if?
这是滑动窗口的灵魂问题。
❌ 如果用 if
if (sumWindow >= target) {
// 只收缩一次
}
问题在于:
- 当前窗口可能 还能继续收缩
- 用
if只会错过更短的合法区间
✅ 用 while 的含义是:
只要窗口仍然合法,就一直缩,
直到它刚好不合法为止。
这样才能保证:
- 当前
right固定时 - 找到的就是 最短合法区间
八、为什么这是“双指针”?
left:只增不减right:只增不减- 每个指针最多走
n步
👉 整体时间复杂度 O(n)
这正是“双指针优化暴力枚举”的经典范式。
九、时间 & 空间复杂度分析
⏱️ 时间复杂度
left和right各遍历数组一次
👉 O(n)
🧠 空间复杂度
- 只用了常数变量
👉 O(1)
十、如何一眼看出这是滑动窗口题?
看到这些关键词,请直接条件反射:
- 连续子数组
- 最小 / 最大长度
- 满足某个条件(和 ≥ target)
- 数组元素为正数
👉 90% 是滑动窗口 + 双指针
十一、通用滑动窗口模板
for (right ...) {
扩张窗口
while (窗口满足条件) {
更新答案
收缩窗口
}
}
十二、一句话总结
滑动窗口的本质,不是“固定区间”,
而是“在合法条件下,把区间压缩到极致”。