算法滑动窗口系列-Day8-长度最小的子数组

文章目录

  • 前言
  • 一、题目链接
  • 二、题目描述
  • 三、解法一(暴力求解会超时)
  • 四、解法二(滑动窗口)
  • 五、代码编写


前言

滑动窗口第一题


一、题目链接

长度最小的子数组


二、题目描述

算法滑动窗口系列-Day8-长度最小的子数组_第1张图片


三、解法一(暴力求解会超时)

算法思路:
[从前往后]枚举数组中的任意一个元素,把它当成起始位置。然后从这个「起始位置」开始,然后寻找一段最短的区间,使得这段区间的和[大于等于]目标值。将所有元素作为起始位置所得的结果中,找到「最小值」即可。

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        // 记录结果
        int ret = INT_MAX;
        int n = nums.size();
        // 枚举出所有满足和大于等于 target 的子数组[start, end]
        // 由于是取到最小,因此枚举的过程中要尽量让数组的长度最小
        // 枚举开始位置
        for (int start = 0; start < n; start++) {
            int sum = 0; // 记录从这个位置开始的连续数组的和
            // 寻找结束位置
            for (int end = start; end < n; end++) {
                sum += nums[end];  // 将当前位置加上
                if (sum >= target) // 当这段区间内的和满足条件时
                {
                    // 更新结果,start 开头的最短区间已经找到
                    ret = min(ret, end - start + 1);
                    break;
                }
            }
        }
        // 返回最后结果
        return ret == INT_MAX ? 0 : ret;
    }
};

算法滑动窗口系列-Day8-长度最小的子数组_第2张图片


四、解法二(滑动窗口)

算法思路:
由于此问题分析的对象是「一段连续的区间」,因此可以考虑「滑动窗口」的思想来解决这道题。让滑动窗口满足:从 i 位置开始,窗口内所有元素的和小于 target (那么当窗口内元素之和第一次大于等于目标值的时候,就是 i 位置开始,满足条件的最小长度)。

做法:将右端元素划入窗口中,统计出此时窗口内元素的和:

▪ 如果窗口内元素之和大于等于 target :更新结果,并且将左端元素划出去的同时继续判断是否满足条件并更新结果(因为左端元素可能很小,划出去之后依旧满足条件)
▪ 如果窗口内元素之和不满足条件: right++ ,另下一个元素进入窗口。

为何滑动窗口可以解决问题,并且时间复杂度更低?

▪ 这个窗口寻找的是:以当前窗口最左侧元素(记为 left1 )为基准,符合条件的情况。也就是在这道题中,从 left1 开始,满足区间和 sum >= target 时的最右侧(记为right1 )能到哪里。

▪ 我们既然已经找到从 left1 开始的最优的区间,那么就可以大胆舍去 left1 。但是如果继续像方法一一样,重新开始统计第二个元素( left2 )往后的和,势必会有大量重复的计算(因为我们在求第一段区间的时候,已经算出很多元素的和了,这些和是可以在计算下次区间和的时候用上的)。

▪ 此时, rigth1 的作用就体现出来了,我们只需将 left1 这个值从 sum 中剔除。从right1 这个元素开始,往后找满足 left2 元素的区间(此时 right1 也有可能是满足的,因为 left1 可能很小。sum 剔除掉 left1 之后,依旧满足大于等于target )。这样我们就能省掉大量重复的计算。

▪ 这样我们不仅能解决问题,而且效率也会大大提升。时间复杂度:虽然代码是两层循环,但是我们的 left 指针和 right 指针都是不回退的,两者最多都往后移动 n 次。因此时间复杂度是 O(N) 。


五、代码编写

C++完整代码:

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int n = nums.size(), sum = 0, len = INT_MAX;
        for (int left = 0, right = 0; right < n; right++) {
            sum += nums[right];   // 进窗口
            while (sum >= target) // 判断
            {
                len = min(len, right - left + 1); // 更新结果
                sum -= nums[left++];              // 出窗口
            }
        }
        return len == INT_MAX ? 0 : len;
    }
};

运行结果:
算法滑动窗口系列-Day8-长度最小的子数组_第3张图片

Java完整代码:

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int n = nums.length, sum = 0, len = Integer.MAX_VALUE;
        for (int left = 0, right = 0; right < n; right++) {
            sum += nums[right]; // 进窗口
            while (sum >= target) // 判断
            {
                len = Math.min(len, right - left + 1); // 更新结果
                sum -= nums[left++]; // 出窗口
            }
        }
        return len == Integer.MAX_VALUE ? 0 : len;
    }
}

运行结果:
算法滑动窗口系列-Day8-长度最小的子数组_第4张图片


你可能感兴趣的:(#,滑动窗口,算法)