LeetCode 209. 长度最小的子数组 双指针 滑动窗口

741 阅读2分钟

题目描述

题目来源:力扣(LeetCode)
题目链接:LeetCode 209. 长度最小的子数组

给定一个含有 n 个正整数的数组和一个正整数 target
找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0
示例 1:

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

示例 2:

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

提示:

  • 1 <= target <= 109
  • 1 <= nums.length <= 105
  • 1 <= nums[i] <= 105

解题方法一:双指针

思路

利用双指针 leftrightleft 指向当前子数组起始位置,right 指向当前子数组结束位置的后一个位置。
因为 nums 中的元素都为正数,那么子数组 [left, right) 的元素和必然大于子数组 [left + k, right) 的元素和,其中 k > 0,所以要想满足子数组元素和大于 target ,第二个子数组的结束位置必然位于第一个子数组结束位置的右侧。

代码

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        // n 为 nums 的长度,ans 为满足要求的数组的最小长度(初始化为 n + 1)
        int n = nums.size(), ans = n + 1;
        // 子数组 [left, right) 可能满足要求,其元素和为 sum
        int left = 0, right = 0, sum = 0;
        // 子数组初始位置有效
        while(left < n) {
            // 固定子数组初始位置,如果 sum 小于 target 就不断扩展结束位置
            while(sum < target && right < n) {
                // 更新 sum
                sum += nums[right];
                // 扩展 right
                ++right;
            }
            if(sum >= target) {
                // 满足要求,更新 ans
                ans = min(ans, right - left);
            }else if(right == n) {
                // 初始位置在 left 右侧的子数组的元素和必然小于 target
                break;
            }
            // 更新 sum 为下一个子数组的初始元素和
            sum -= nums[left];
            // 右移子数组起始位置
            ++left;
        }
        // 如果ans 等于 n + 1,那么没有满足要求的子数组
        return ans == n + 1? 0 : ans;
    }
};

解题方法二:滑动窗口

思路

以 right 为结束位置的元素之和大于等于 target 的滑动窗口可能有多个,为了让这个滑动窗口的宽度最小,在满足要求的条件下,必须从窗口最左侧不断尝试剔除元素。

代码

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        // n 为 nums 的长度,ans 为满足要求的数组的最小长度(初始化为 n + 1)
        int n = nums.size(), ans = n + 1;
        // left 为滑动窗口的起始位置,right 为滑动窗口的结束位置,sum 为滑动窗口内元素的和
        int left = 0, right = -1, sum = 0;
        // 滑动窗口结束位置有效
        while(++right < n) {
            // 更新 sum
            sum += nums[right];
            // 当前滑动窗口满足要求
            if(sum >= target) {
                // 不断右移滑动窗口起始位置,使得当前以 right 为结束位置的滑动窗口的元素和在大于等于 target 的条件下最小
                while(sum >= target) {
                    // 向右侧移动 left
                    sum -= nums[left++];
                }
                // 更新 ans
                ans = min(ans, right - left + 2);
            }
        }
        // 如果ans 等于 n + 1,那么没有满足要求的子数组
        return ans == n + 1? 0 : ans;
    }
};