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 右移前,用当前窗口长度尝试更新最小值
相关推荐
倒头就睡的小比特4 天前
算法竞赛C++常用的STL
c++·算法
小羊没烦恼!4 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
猎头南楼4 天前
知识社区推荐系统实践:新用户冷启动与长短期兴趣建模的挑战 资深推荐算法工程师
人工智能·深度学习·算法·机器学习
旖旎夜光4 天前
力控面试题 01.01: 判定字符是否唯一(位运算) —— 题解
c++·学习·算法·leetcode·力控
wzdark4 天前
大规模并行计算中的负载均衡算法研究4
算法
Because_of_Her14 天前
并查集-听课笔记
笔记·算法·并查集
码流子4 天前
高速公路安全监测实践:碰撞监测预警+物联网底座,从感知到处置的闭环
大数据·人工智能·物联网·算法·架构
another heaven4 天前
【算法/C++ MD5算法能否逆解码?原理、C++实现与同类哈希算法对比】
c++·算法·哈希算法
wzdark4 天前
从算法设计模式看编程思维的抽象能力4
算法
2601_962218614 天前
万象生鲜系统称重自动多退少补算法解决生鲜非标品痛点
大数据·数据库·人工智能·python·算法