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

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

用两个指针 leftright 维护一个窗口:

  • 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]

注意步骤 5while 让窗口从 [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) 每个元素最多被 leftright 各访问一次
空间复杂度 O(1) 只用了几个变量

总结

要点 说明
min 初始化为 nums.length + 1 作为"未找到"的标志,最终通过三元表达式返回 0 或 min
while 而非 if 确保找到以当前 right 结尾的最短合法子数组
窗口缩小时更新 min 每次 left 右移前,用当前窗口长度尝试更新最小值
相关推荐
Hwang2524 小时前
Attention 机制 02 - Add&Norm 残差机制
算法
计算机安禾4 小时前
【c++面向对象编程】第48篇:Lambda表达式与std::function:OOP中的函数式编程
java·c++·算法
手写码匠4 小时前
【实战评测】华为云 MaaS 平台 DeepSeek 大模型推理服务 + Dify 一键部署全攻略
人工智能·深度学习·算法·aigc
咪饭只吃一小碗4 小时前
JS算法基础: 常用方法整理
算法·程序员
z200509305 小时前
今日算法(回溯算法)
数据结构·算法
毅炼5 小时前
今日LeetCode 摸鱼打卡
java·算法·leetcode
m0_629494735 小时前
LeetCode 热题 100-----28. 两数相加
数据结构·算法·leetcode·链表
菜菜的顾清寒5 小时前
力扣HOT100(25)环形链表
算法·leetcode·链表
学不懂飞行器6 小时前
【2024电赛H题硬核解析】自动行驶小车满分对策:多路灰度循迹与陀螺仪“交替盲走”融合算法(附源码)
stm32·单片机·嵌入式硬件·算法·电赛