在问 · 算法题解

209. 长度最小的子数组

题目描述

输入正整数 target 和一个由 n 个正整数组成的数组 nums。

在所有元素和不小于 target 的连续子数组里,找出长度最短的一个,返回它的长度。

如果没有任何连续子数组满足条件,返回 0。

示例 1
输入:target = 7, nums = [2,3,1,2,4,3]
输出:2

解释:[4,3] 的和为 7,长度 2 是最短的。

示例 2
输入:target = 4, nums = [1,4,4]
输出:1

解释:单个元素 4 已经满足条件。

示例 3
输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0

解释:整个数组的和只有 8,小于 11,没有满足条件的子数组。

数据范围

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

进阶:如果你已经写出 O(n) 的做法,再试试设计一个 O(n log(n)) 的做法。

解题思路:滑动窗口

所有元素为正,前缀和严格递增,这是可以二分的根本原因:对每个左端可在前缀和上二分出最短右端,O(n log n)。同样的单调性还允许更快的滑动窗口:右端 right 逐个加入,一旦窗口和 s >= target,就记录长度并不断弹出左端,直到 s < target。左端只向右走,合计 O(n)。

为什么正确

每次内层循环开始前,对当前 right,[left, right] 是左端不小于之前任何合法左端的候选;被弹出的左端已经找到了它能形成的最短合法窗口。

复杂度分析

滑动窗口时间 O(n)、空间 O(1);前缀和 + 二分 O(n log n)。

易错点

内层要用 while 而不是 if;ans 初值用 n+1 或无穷大,最后要把“未更新”转成 0。只有元素全为正时这个方法才成立。

参考代码

Python、Java、C++、Go、JavaScript、TypeScript 六种语言,逻辑一致。

class Solution:
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:
        ans = len(nums) + 1
        s = left = 0
        for right, x in enumerate(nums):
            s += x
            while s >= target:
                ans = min(ans, right - left + 1)
                s -= nums[left]
                left += 1
        return ans if ans <= len(nums) else 0

载入编辑器,自己跑一遍 →

其他解法

两种解法都依赖“元素全为正数”:前缀和二分利用前缀和单调,对每个起点独立二分;滑动窗口利用左右端点都只向右移动,整体 O(n)。

解法 2:前缀和 + 二分查找 时间 O(n log n),空间 O(n)

先算前缀和 pre,pre[i] 是前 i 个数的和。因为所有数都是正数,pre 严格递增。枚举子数组的起点 i,在 pre 中二分查找第一个满足 pre[j] ≥ pre[i] + target 的下标 j,子数组 [i, j) 的长度 j - i 就是以 i 开头时的最短长度,取所有起点中的最小值。

对应进阶要求的 O(n log n) 解法,展示了“前缀和单调就能二分”的思路。比滑动窗口多一个 log 因子和 O(n) 空间。

from bisect import bisect_left
from itertools import accumulate
from typing import List

class Solution:
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:
        n = len(nums)
        pre = [0] + list(accumulate(nums))
        ans = n + 1
        for i in range(n):
            j = bisect_left(pre, pre[i] + target)
            if j <= n:
                ans = min(ans, j - i)
        return ans if ans <= n else 0

二分查找 专题的其他题目

查看 二分查找 全部题目 →