题目描述
题目来源:力扣(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 <= 1091 <= nums.length <= 1051 <= nums[i] <= 105
解题方法一:双指针
思路
利用双指针 left 和 right ,left 指向当前子数组起始位置,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;
}
};