力扣 209. 长度最小的子数组:滑动窗口解法完整解析

题目描述​

给定一个含有 n 个正整数的数组和一个正整数 target ,找出该数组中满足其和 ≥ target 的长度最小的 连续子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。​

示例​

  • 输入:target = 7, nums = [2,3,1,2,4,3]
  • 输出:2(解释:子数组 [4,3] 的和为 7,长度最小)
  • 输入:target = 11, nums = [1,1,1,1,1,1,1,1]
  • 输出:0(无满足条件的子数组)

二、解题思路:滑动窗口(双指针)法​

你的代码采用了 滑动窗口(双指针) 这一高效解法,核心思想是:用一个 "窗口"(左指针 j 、右指针 i 划定的连续子数组)动态调整范围,始终维护窗口内元素和 sum ≥ target,并记录最小窗口长度。​

为什么选择滑动窗口?​

  • 暴力解法(枚举所有子数组)时间复杂度为 O(n²),对于大规模数组效率过低;
  • 滑动窗口通过 "右指针扩张窗口、左指针收缩窗口" 的方式,仅遍历数组一次,时间复杂度优化至 O(n),空间复杂度为 O(1)(无需额外辅助空间)。

三、代码逐行解析

cpp 复制代码
int minSubArrayLen(int target, vector<int>& nums) {
    int j = 0; // 左指针:窗口起始位置
    int result = nums.size(); // 初始化最小长度为数组总长度(最坏情况)
    int sum = 0; // 窗口内元素的和
    int sub = 0; // 当前窗口的长度
    int a = 0; // 数组所有元素的总和(用于判断是否无解)

    // 右指针i扩张窗口,遍历数组每个元素
    for (int i = 0; i < nums.size(); i++) {
        sum += nums[i]; // 右指针加入当前元素,扩大窗口和
        a += nums[i]; // 累加计算数组总长度

        // 当窗口和≥target时,尝试收缩左指针,寻找最小窗口
        while (sum >= target) {
            sub = i - j + 1; // 计算当前窗口长度(i-j+1:闭区间长度)
            result = min(result, sub); // 更新最小窗口长度
            sum -= nums[j]; // 左指针移出当前元素,缩小窗口和
            j++; // 左指针右移,继续尝试收缩
        }
    }

    // 若数组总和<target,说明无解,返回0
    if (a < target) {
        return 0;
    }
    return result; // 返回最小窗口长度
}

关键步骤拆解

  1. 初始化变量
    • 左指针 j 从 0 开始,确保窗口从数组起始位置出发;
    • result 初始化为数组长度,因为最小窗口长度不可能超过数组本身;
    • sum 记录当前窗口内元素和,a 记录数组总元素和(用于后续判断是否存在解)。
  1. 右指针扩张窗口
    • 循环中 i 作为右指针,依次将数组元素加入窗口(sum += nums[i]),持续扩大窗口范围,直到窗口和 sum ≥ target。
  1. 左指针收缩窗口(核心逻辑)
    • 当 sum ≥ target 时,进入 while 循环:
      • 计算当前窗口长度 sub = i - j + 1(注意闭区间长度公式);
      • 用 min(result, sub) 更新最小窗口长度,确保 result 始终存储当前最小値;
      • 移除左指针指向的元素(sum -= nums[j]),并右移左指针 j,收缩窗口,尝试寻找更短的有效窗口。
    • 这里用 while 而非 if,是因为收缩一次后,窗口和可能仍≥target,需要持续收缩(例如数组 [2,2,3],target=5:初始窗口 [2,2,3] 和为 7,收缩左指针后 [2,3] 和为 5,仍满足条件,需继续更新最小长度)。
  1. 无解判断
    • 循环结束后,若 a < target(数组所有元素和都小于目标值),说明不存在有效子数组,返回 0;否则返回 result。

四、测试用例验证

测试用例 1:存在有效子数组
  • 输入:target=7, nums=[2,3,1,2,4,3]
  • 过程:
    • i=0(nums [0]=2):sum=2 <7,不进入 while;
    • i=1(nums [1]=3):sum=5 <7,不进入 while;
    • i=2(nums [2]=1):sum=6 <7,不进入 while;
    • i=3(nums [3]=2):sum=8 ≥7,进入 while:
      • sub=3-0+1=4,result=4;sum=8-2=6,j=1;
      • sum=6 <7,退出 while;
    • i=4(nums [4]=4):sum=6+4=10 ≥7,进入 while:
      • sub=4-1+1=4,result=min(4,4)=4;sum=10-3=7,j=2;
      • sub=4-2+1=3,result=3;sum=7-1=6,j=3;
      • sum=6 <7,退出 while;
    • i=5(nums [5]=3):sum=6+3=9 ≥7,进入 while:
      • sub=5-3+1=3,result=min(3,3)=3;sum=9-2=7,j=4;
      • sub=5-4+1=2,result=2;sum=7-4=3,j=5;
      • sum=3 <7,退出 while;
  • 最终 result=2,符合预期。
测试用例 2:无有效子数组
  • 输入:target=11, nums=[1,1,1,1,1,1,1,1]
  • 过程:循环结束后 a=8 <11,返回 0,符合预期。

五、优化与注意事项

1. 代码优化点
  • 变量 a 可省略:循环中若 result 始终未更新(即未找到有效窗口),说明无解。可将 result 初始化为 INT_MAX,循环结束后判断 result == INT_MAX 则返回 0,无需额外维护 a:
cpp 复制代码
int result = INT_MAX; // 初始化为最大值
// ... 循环逻辑不变 ...
return result == INT_MAX ? 0 : result; // 简化判断
  • 这样修改后,代码更简洁,减少不必要的变量存储。

    1. 边界条件注意​
  • 数组长度为 1:若 nums[0] ≥ target,返回 1;否则返回 0;

  • target 等于数组中某个元素:此时最小窗口长度为 1(例如 target=3, nums=[1,3,2],直接返回 1);

  • 所有元素和恰好等于 target:此时最小窗口长度为数组长度。

  • 六、算法总结​

  • 时间复杂度:O(n),左指针和右指针均最多遍历数组一次,无嵌套循环;

  • 空间复杂度:O(1),仅使用常数个变量;

  • 核心优势:相比暴力解法,滑动窗口避免了重复计算子数组和,大幅提升效率,是解决 "连续子数组和 / 长度" 类问题的经典方法。

  • 滑动窗口的核心思路是 "动态调整窗口范围,维护有效状态",类似的题目还包括 "最长无重复子串""最小覆盖子串" 等,掌握这种思想可以举一反三,高效解决一系列数组区间问题。​

相关推荐
灵感__idea1 小时前
Hello 算法:众里寻她千“百度”
前端·javascript·算法
Wect11 小时前
LeetCode 130. 被围绕的区域:两种解法详解(BFS/DFS)
前端·算法·typescript
NAGNIP1 天前
一文搞懂深度学习中的通用逼近定理!
人工智能·算法·面试
颜酱1 天前
单调栈:从模板到实战
javascript·后端·算法
CoovallyAIHub1 天前
仿生学突破:SILD模型如何让无人机在电力线迷宫中发现“隐形威胁”
深度学习·算法·计算机视觉
CoovallyAIHub1 天前
从春晚机器人到零样本革命:YOLO26-Pose姿态估计实战指南
深度学习·算法·计算机视觉
CoovallyAIHub1 天前
Le-DETR:省80%预训练数据,这个实时检测Transformer刷新SOTA|Georgia Tech & 北交大
深度学习·算法·计算机视觉
CoovallyAIHub1 天前
强化学习凭什么比监督学习更聪明?RL的“聪明”并非来自算法,而是因为它学会了“挑食”
深度学习·算法·计算机视觉
CoovallyAIHub1 天前
YOLO-IOD深度解析:打破实时增量目标检测的三重知识冲突
深度学习·算法·计算机视觉
NAGNIP2 天前
轻松搞懂全连接神经网络结构!
人工智能·算法·面试