滑动窗口-长度最小的子数组

55 阅读2分钟

209.长度最小的子数组

力扣题目链接

描述

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

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

分析

  • 解法1:使用双重循环查找。找出每个以nums[i]开头且总和大于等于target的数组,返回长度最小的数组长度。时间复杂度为O(N^2)
  • 解法2:使用滑动窗口算法。由left和right来维护窗口的长度

解法(滑动窗口算法)

  1. 初始化left=0,sum=0,minLength=Integer.MAX_VALUE;
  2. 使用for循环遍历滑动右窗口i,循环逻辑如下:
  • sum+=nums[i]。
  • 若sum<target, i继续右滑,left不变
  • 若sum>=target,循环(while(sum >= target)),sum-=nums[left],移动left, 计算当前窗口的长度并和minLength比较,保存最小的。

时间复杂度:O(N)

提问:存在两次循环时间复杂度为什么还是O(N)? 解答:分析时间复杂度不能只看循环。看看整个过程left和right两个变量的执行的操作总数都是n ,n+n=2n,所以依旧是O(N)级别的。

代码


    public int minSubArrayLen(int target, int[] nums) {
        int left = 0;
        int curSum = 0;
        int minLength = Integer.MAX_VALUE;
        //遍历每个右边界,找到每个右边界对应之下的最小左边界,

        for (int i = 0; i < nums.length; i++) {
            curSum += nums[i];
            if (curSum < target) {
                continue;
            } else {
                while (curSum >= target) {
                    minLength = Math.min(minLength, i - left + 1);
                    curSum -= nums[left];
                    left++;
                }
            }
        }

        if (minLength == Integer.MAX_VALUE) {
            return 0;
        }
        return minLength;
    }

总结

滑动窗口算法巧在循环的每一步,right在向右滑动之后,根据上一步记录的left来进行选择性滑动,从而计算得到left每滑动一步后的窗口大小,并把最小值记录下来。