209. 长度最小的子数组(C++)

题目

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

示例:

输入:s = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。

进阶:

  • 如果你已经完成了 O(n) 时间复杂度的解法, 请尝试 O(n log n) 时间复杂度的解法。

分析与题解

暴力解法

循环嵌套,以数组的每个元素为起点,不断累加直至元素和sum >= s,这是我们再记录子序列元素个数的长度。每次内层循环后都需要跟当前最小子序列长度进行比较。

完整代码如下:

class Solution {
public:
    int minSubArrayLen(int s, vector<int>& nums) {
        int n = nums.size();
        int minLen = INT_MAX;
        int subLen = 0;
        for (int i = 0; i < n; i++) {
            int sum = 0;
            for (int j = i; j < n; j++) {
                // 设置中止条件
                sum += nums[j];
                if (sum >= s) {
                    subLen = j - i + 1;
                    minLen = min(minLen, subLen);
                    break;
                }
            }
        }

        // 最后筛选没有可行长度的极端情况
        return minLen == INT_MAX ? 0 : minLen;
    }
};

滑动窗口

类似双指针法的变种,所谓滑动窗口,「就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果」

图片

滑动窗格内包含的就是满足sum >= s的子序列,核心代码如下:

while (sum >= s) {
                subLen = j - i + 1;
                minLen = min(minLen, subLen);
                // 满足条件后移动滑动窗口的左边界
                sum -= nums[i];
                i++;
            }

初始找到一个符合条件的滑动窗格后,考虑依次删除窗格左侧的元素并使用while循环再次判断题目条件。当退出内层while循环后,再从数组的下一个起点开始进行讨论。对应于暴力解,可以理解为在外层循环中穿插的移动滑动窗口左侧边界,来减少算法复杂度。

完整代码如下:

class Solution {
public:
    int minSubArrayLen(int s, vector<int>& nums) {
        int n = nums.size();
        // i表示滑动窗口的左边界下标
        int i = 0;
        int minLen = INT_MAX;
        int subLen;
        int sum = 0;
        for (int j = 0; j < n; j++) {
            sum += nums[j];
            while (sum >= s) {
                subLen = j - i + 1;
                minLen = min(minLen, subLen);
                // 满足条件后移动滑动窗口的左边界
                sum -= nums[i];
                i++;
            }
        }
        // 如果minLen没有赋值,说明没有符合条件的子序列
        return minLen == INT_MAX ? 0 : minLen;
    }
};
posted @ 2020-12-30 16:59  脱线森林`  阅读(128)  评论(0编辑  收藏  举报