Q209 长度最小的子数组

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

我看到这个问题,首先发现它是连续的、并且要求区间大小,会想到用前缀和操作——前缀和可以快速求出连续元素的值,但我对它的时间复杂度了解程度不够好。

核心结论:滑动窗口如果是求值的话,右窗口滑动的同时,大概率会有某个参数要进行操作,后续进行比较从而操作左窗口,这是一个连续过程,所以题目得是连续问题。

因为数组元素都是正数,可以使用滑动窗口。右指针不断扩展并累加区间和;当区间和大于等于 target 时,持续移动左指针缩短区间并更新最小长度。两个指针都只向右移动,时间复杂度是 $O(n)$。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int left = 0;//左指针
        int right = 0;//右指针
        int sz = nums.size();
        int res = sz+1;
        int sum = 0;
        while(right != sz){
            sum += nums[right];
            while(sum >= target){//注意,判断left移动是一个连续过程
                res = min(res , right-left+1);
                sum -= nums[left++];
            }
            right++;
        }
        if(res == sz+1){
            return 0;
        }else{
            return res;
        }
    }
};

可以。 设前缀和为 prefix,因为题目中的元素都是正整数,所以 prefix 严格递增。对每个左端点,可以二分查找第一个满足区间和大于等于 target 的右端点。

  • 只用前缀和枚举左右端点:$O(n^2)$
  • 前缀和 + 二分查找:$O(n\log n)$
  • 滑动窗口:$O(n)$

滑动窗口更快的关键也是"元素全为正数":右端点右移时区间和只会增大,左端点右移时区间和只会减小,因此两个指针都不需要回退。

易错或补充:比如数组 [1,1,1,1,100],target = 100,滑动窗口的 res 初始值(sz+1)要够大,否则取不到正确答案。

/q209-%E9%95%BF%E5%BA%A6%E6%9C%80%E5%B0%8F%E7%9A%84%E5%AD%90%E6%95%B0%E7%BB%84/image-1.png

相关内容