每日算法练习:LeetCode 209. 长度最小的子数组 ✅

大家好,我是你们的算法小伙伴。今天我们来练习一道滑动窗口的经典高频题 ------LeetCode 209. 长度最小的子数组。这道题考察滑动窗口(双指针)的核心思想,同时还附带了一个进阶的二分查找优化方案,是面试中考察数组优化的经典题目。


题目描述

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

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

进阶: 如果你已经实现 O(n) 时间复杂度的解法,请尝试设计一个 O(n log n) 时间复杂度的解法。


示例 1:

复制代码
输入:target = 7, nums = [2,3,1,2,4,3]

输出:2

解释:子数组 [4,3] 是该条件下的长度最小的子数组。

示例 2:

复制代码
输入:target = 4, nums = [1,4,4]

输出:1

示例 3:

复制代码
输入:target = 11, nums = [1,1,1,1,1,1,1,1]

输出:0

提示:

  • 1 <= target <= 10^9
  • 1 <= nums.length <= 10^5
  • 1 <= nums[i] <= 10^4

解题思路

核心特性拆解

数组的核心特性:所有元素为正整数

  • 正整数的特性保证了:窗口向右扩张时,和一定增大;窗口向左收缩时,和一定减小,这是滑动窗口成立的关键前提。

方法一:滑动窗口(双指针,O (n))

思路:用左右指针维护一个窗口,记录窗口内元素的和:

  1. 右指针向右移动,将元素加入窗口,累加和;
  2. 当窗口和 ≥ target 时,尝试收缩左指针,尽可能缩小窗口长度,同时更新最小长度;
  3. 重复上述过程,直到右指针遍历完整个数组。

方法二:前缀和 + 二分查找(O (n log n))

思路:利用前缀和数组,将问题转化为「在有序数组中找满足条件的最小长度」,适合数组元素有正有负、无法用滑动窗口的场景,本题作为进阶方案。

  1. 计算前缀和数组 prefix,其中 prefix[i] 表示前 i 个元素的和;
  2. 遍历前缀和数组,对每个 prefix[i],用二分查找找最小的 j > i,使得 prefix[j] - prefix[i] ≥ target,计算窗口长度 j - i 并更新最小值。

代码实现

方法一:滑动窗口

复制代码
class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int n = nums.length;
        int left = 0; // 窗口左边界
        int sum = 0; // 窗口内元素和
        int minLen = Integer.MAX_VALUE; // 记录最小长度
        
        // 右指针遍历数组
        for (int right = 0; right < n; right++) {
            sum += nums[right]; // 右指针右移,累加和
            // 当和 >= target 时,收缩左指针
            while (sum >= target) {
                // 更新最小长度
                minLen = Math.min(minLen, right - left + 1);
                // 左指针右移,减去对应元素
                sum -= nums[left];
                left++;
            }
        }
        
        // 如果minLen未更新,说明无符合条件的子数组,返回0
        return minLen == Integer.MAX_VALUE ? 0 : minLen;
    }
}

方法二:前缀和 + 二分查找

复制代码
class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int n = nums.length;
        int[] prefix = new int[n + 1]; // 前缀和数组,prefix[0] = 0
        // 计算前缀和
        for (int i = 1; i <= n; i++) {
            prefix[i] = prefix[i - 1] + nums[i - 1];
        }
        
        int minLen = Integer.MAX_VALUE;
        // 遍历前缀和,二分查找
        for (int i = 0; i < n; i++) {
            int need = target + prefix[i]; // 需要找到 >= need 的前缀和
            // 二分查找第一个 >= need 的位置
            int left = i + 1, right = n;
            while (left <= right) {
                int mid = left + (right - left) / 2;
                if (prefix[mid] >= need) {
                    right = mid - 1;
                } else {
                    left = mid + 1;
                }
            }
            // 找到符合条件的位置,更新最小长度
            if (left <= n) {
                minLen = Math.min(minLen, left - i);
            }
        }
        
        return minLen == Integer.MAX_VALUE ? 0 : minLen;
    }
}

代码详解

一、滑动窗口法

  1. 指针初始化left = 0(窗口左边界),sum = 0(窗口和),minLen = Integer.MAX_VALUE(初始设为极大值)。
  2. 右指针扩张 :遍历数组,将 nums[right] 加入窗口,累加和。
  3. 左指针收缩 :当 sum >= target 时,不断收缩左指针,每次收缩都更新最小长度,直到 sum < target
  4. 结果处理 :若 minLen 仍为极大值,说明无符合条件的子数组,返回 0,否则返回 minLen

示例 1

模拟target = 7, nums = [2,3,1,2,4,3]

步骤 left right sum 窗口 sum >= 7? minLen 操作
1 0 0 2 [2] MAX right++
2 0 1 5 [2,3] MAX right++
3 0 2 6 [2,3,1] MAX right++
4 0 3 8 [2,3,1,2] 4 sum-=2, left=1
5 1 3 6 [3,1,2] 4 right++
6 1 4 10 [3,1,2,4] 4 → 4 sum-=3, left=2
7 2 4 7 [1,2,4] 3 sum-=1, left=3
8 3 4 6 [2,4] 3 right++
9 3 5 9 [2,4,3] 3 → 3 sum-=2, left=4
10 4 5 7 [4,3] 3 → 2 sum-=4, left=5
11 5 5 3 [3] 2 循环结束

最终返回 2,与示例结果一致。

二、前缀和 + 二分查找法

  1. 前缀和计算prefix[0] = 0prefix[i] = prefix[i-1] + nums[i-1],保证前缀和数组严格递增(数组元素为正)。
  2. 二分查找 :对每个 prefix[i],计算需要的目标和 need = target + prefix[i],在 i+1n 区间内二分查找第一个 >= needprefix[j],窗口长度为 j - i
  3. 优势 :不依赖数组元素全为正,适用于更通用的场景,时间复杂度 O(n log n)

复杂度分析

解法 时间复杂度 空间复杂度 适用场景
滑动窗口法 O(n) O(1) 数组元素全为正,最优解
前缀和 + 二分查找 O(n log n) O(n) 数组元素有正有负,通用解法

总结

  1. 核心考点 :本题的核心是 ** 滑动窗口(双指针)** 的应用,利用数组元素全为正的特性,将暴力枚举的O(n²)复杂度优化到O(n),是面试中考察数组优化的经典题目。
  2. 关键逻辑:滑动窗口的核心是「右指针扩张,左指针收缩」,只有当窗口和满足条件时才收缩左指针,保证每个元素最多被访问两次,时间复杂度线性。
  3. 进阶拓展:前缀和 + 二分查找是通用解法,不依赖元素全为正,适合处理更复杂的子数组和问题,是面试中拓展思路的好方向。
  4. 易错点
    • 初始minLen设为Integer.MAX_VALUE,避免遗漏边界情况;
    • 窗口长度计算为right - left + 1,不要漏加1
    • 最终返回时需判断minLen是否更新,未更新则返回0

今天的每日算法练习就到这里,我们明天再见!👋

相关推荐
_日拱一卒2 小时前
LeetCode:除了自身以外数组的乘积
数据结构·算法·leetcode
计算机安禾2 小时前
【数据结构与算法】第36篇:排序大总结:稳定性、时间复杂度与适用场景
c语言·数据结构·c++·算法·链表·线性回归·visual studio
SatVision炼金士2 小时前
合成孔径雷达干涉测量(InSAR)沉降监测算法体系
算法
wuweijianlove2 小时前
算法稳定性与数值误差传播研究的技术2
算法
计算机安禾3 小时前
【数据结构与算法】第35篇:归并排序与基数排序
c语言·数据结构·vscode·算法·排序算法·哈希算法·visual studio
爱码小白3 小时前
MySQL 单表查询练习题汇总
数据库·python·算法
橘颂TA3 小时前
【笔试】算法的暴力美学——牛客 NC213140 :除2!
c++·算法·结构与算法
yoyobravery3 小时前
蓝桥杯第15届单片机满分
单片机·职场和发展·蓝桥杯
汀、人工智能3 小时前
[特殊字符] 第66课:跳跃游戏
数据结构·算法·数据库架构·图论·bfs·跳跃游戏