题目
给定一个含有 n
个正整数的数组和一个正整数 target
。
找出该数组中满足其总和大于等于target
的长度最小的 子数组 [nums(l), nums(l+1), ..., nums(r-1), nums(r)]
,并返回其长度**。** 如果不存在符合条件的子数组,返回 0
。
示例 1:
输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3]是该条件下的长度最小的子数组。
示例 2:
输入:target = 4, nums = [1,4,4]
输出:1
示例 3:
输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0
解析
采用滑动窗口的方法,连续子数组可以表示为 [nums(left)......nums(right)],即从第 left 项到第 right 项。
- 当下标为[left...right] 的子数组和 >= target,如果此时扩张窗口,>= target的条件依然满足(因为是数组和target都是正整数),但背离"最小长度"的要求。
- 所以选择收缩窗口:当下标为[left...right] 的子数组和 >= target时,left 继续右移,并更新最小长度,直到不再满足>= target的条件。
- 当窗口[left...right]的子数组和 < target时,right 右移,扩大窗口,更新子数组和,直到条件重新满足。
总结
扩张窗口:为了找到一个可行解,找到了就不再扩张,因为扩张不再有意义。
收缩窗口:在长度上优化该可行解,直到条件被破坏。
继续寻找下一个可行解,然后再优化到不能优化......
作者:笨猪爆破组
来源:力扣(LeetCode)
答案
javascript
/**
* @param {number} target
* @param {number[]} nums
* @return {number}
*/
var minSubArrayLen = function(target, nums) {
const n = nums.length;
let ans = n + 1; //初始化答案为不可能的值
let s = 0, left = 0;
for(let right = 0; right < n; right++) { //移动右指针,扩大窗口
s += nums[right];
while(s >= target) { //满足子数组和大于等于target条件的情况下
ans = Math.min(ans, right - left + 1); //更新最小长度值
s -= nums[left];
left++; //移动左指针,缩小窗口
}
}
return ans > n ? 0 : ans; //若ans大于数组长度,则返回0
};
复杂度分析
时间复杂度:O(n),其中 n 为 nums 的长度。虽然写了个二重循环,但是内层循环中对 left 加一的总执行次数不会超过 n 次,所以总的时间复杂度为 O(n)。
空间复杂度:O(1),仅用到若干额外变量。
作者:灵茶山艾府
来源:力扣(LeetCode)