LeetCode 209 · 滑动窗口经典题型

在数组中找大于等于目标值的最小子数组的长度

LeetCode 209 · 滑动窗口经典题型

题目描述

给定一个含有 n 个正整数的数组和一个正整数 target。

找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr],并返回其长度。如果不存在符合条件的子数组,返回 0。

解题思路

核心思路:用滑动窗口找到以每个元素开头的最小子数组,不断更新最小长度。

第一步:初始化最小长度

javascript 复制代码
let min = nums.length + 1;

为什么让 min = nums.length + 1 而不是 nums.length?

因为题目要求:不存在符合条件的子数组时,返回 0。

假设令 min = nums.length,来看一个问题场景:

步骤 操作 sum min
1 遍历完整个数组 sum < target nums.length(未更新)

遍历结束后 sum 仍然小于 target,说明不存在符合条件的子数组,应该返回 0。

但此时 min = nums.length,如果直接返回 min,就返回了错误的值。

解决方案:令 min = nums.length + 1,用它作为"未找到"的标志。

最终返回时:

javascript 复制代码
return min === nums.length + 1 ? 0 : min;

这样只要 min 没被更新过(还是初始值),就说明没找到,返回 0。

第二步:滑动窗口找最小长度

用两个指针 left 和 right 维护一个窗口:

  • left:确定子数组的开头
  • right:确定子数组的结尾,不断右移使 sum ≥ target
javascript 复制代码
let left = 0;
let right = 0;
let sum = 0;
let min = nums.length + 1;

for (; right < nums.length; right++) {
    sum += nums[right];

    while (sum >= target) {
        min = Math.min(right - left + 1, min);
        sum -= nums[left];
        left++;
    }
}

return min === nums.length + 1 ? 0 : min;

关键问题:为什么用 while 而不是 if?

javascript 复制代码
while (sum >= target) {  // ← 为什么是 while,不是 if?
    min = Math.min(right - left + 1, min);
    sum -= nums[left];
    left++;
}

因为当当前子数组的和 sum ≥ target 时,就可以开始尝试缩短子数组,看看去掉左边元素后是否仍然满足条件。

如果用 if:只会缩短一次就跳出,可能错过更短的合法子数组。

用 while:会一直缩短,直到 sum < target,确保找到的是以当前 right 结尾的最短合法子数组。

图解示例

以 nums = [2, 3, 1, 2, 4, 3],target = 7 为例:

步骤 窗口 sum 操作
1 [2] 2 right 右移
2 [2, 3] 5 right 右移
3 [2, 3, 1] 6 right 右移
4 [2, 3, 1, 2] 8 ≥ 7 min = 4,left 右移 → sum = 6 < 7
5 [3, 1, 2, 4] 10 ≥ 7 min = 4,left 右移 → [1, 2, 4] sum = 7 ≥ 7,min = 3 ,left 右移 → [2, 4] sum = 6 < 7
6 [2, 4, 3] 9 ≥ 7 min = 3,left 右移 → [4, 3] sum = 7 ≥ 7,min = 2 ,left 右移 → [3] sum = 3 < 7

最终结果:min = 2(子数组 [4, 3])

注意步骤 5 :while 让窗口从 [3, 1, 2, 4] 逐步缩短到 [2, 4],中间经过 [1, 2, 4](sum = 7 ≥ 7),所以 min 能被更新为 3。如果用 if 就会错过这一步。

完整代码

javascript 复制代码
var minSubArrayLen = function (target, nums) {
    let left = 0;
    let sum = 0;
    let min = nums.length + 1;

    for (let right = 0; right < nums.length; right++) {
        sum += nums[right];

        while (sum >= target) {
            min = Math.min(right - left + 1, min);
            sum -= nums[left];
            left++;
        }
    }

    return min === nums.length + 1 ? 0 : min;
};

复杂度分析

复杂度 值 说明
时间复杂度 O(n) 每个元素最多被 left 和 right 各访问一次
空间复杂度 O(1) 只用了几个变量

总结

要点 说明
min 初始化为 nums.length + 1 作为"未找到"的标志,最终通过三元表达式返回 0 或 min
while 而非 if 确保找到以当前 right 结尾的最短合法子数组
窗口缩小时更新 min 每次 left 右移前,用当前窗口长度尝试更新最小值
相关推荐
Pniubi5 分钟前
力扣438.找到字符串中所有字母异位词
算法·leetcode·职场和发展
東隅已逝,桑榆非晚3 小时前
算法6. 双指针法-查找总价格为目标的两个商品
c++·学习·算法
Eyfcom3 小时前
〖物联网终端实战 01〗UWB 室内高精度定位:仓储 AGV 与人员的厘米级轨迹追踪
物联网·算法·物流系统·快递系统·跨境物流快递系统·物联网终端实战
bro_Java6664 小时前
《排序的底层逻辑:比较、交换与分治》
java·算法·排序算法
爱喝雪碧的可乐4 小时前
MATLAB
开发语言·算法·matlab
叼烟扛炮4 小时前
Linux 学习笔记(六十):单线程的逆袭——epoll 高并发服务器实战
linux·服务器·网络·笔记·学习·算法
careathers5 小时前
【数据结构】基于比较的排序算法 - 交换排序
数据结构·算法·排序算法
Rabitebla6 小时前
【Linux操作系统】进程篇(十一):进程程序替换——从底层原理到 exec 函数族
linux·服务器·c++·笔记·学习·算法
tellmewhoisi6 小时前
深度学习0:数学知识:正态分布1
人工智能·深度学习·算法
-梅6 小时前
算法(1) 动态规划
算法·动态规划