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 右移前,用当前窗口长度尝试更新最小值
相关推荐
小O的算法实验室12 分钟前
2026年CIE,优化客货协同运输:综合地铁系统的列车容量动态分配
算法
Coder_Shenshen1 小时前
西门子S7CommPlus协议鉴权算法原理与流程详解
网络·后端·算法
硕风和炜2 小时前
【LeetCode: 2492. 两个城市间路径的最小分数 + DFS】
java·算法·leetcode·深度优先·dfs·bfs·并查集
我是一颗柠檬3 小时前
【Java项目技术亮点】加权轮询负载均衡算法
java·算法·负载均衡
灯厂码农3 小时前
C语言动态内存分配完全指南(malloc、calloc、realloc、free)
java·c语言·算法
凯瑟琳.奥古斯特4 小时前
K次取反最大化数组和解法(力扣1005)
开发语言·c++·算法·leetcode·职场和发展
Jerry4 小时前
LeetCode 203. 移除链表元素
算法
地平线开发者5 小时前
征程 6 | 工具链 QAT ObserverBase 源码解析
算法
地平线开发者5 小时前
【地平线 征程 6 工具链进阶教程】QAT 训练常见问题和排查
算法