每日刷题第5天 2021.12.30
和大于等于 target 的最短子数组
- 难度:中等
- 方法:滑动窗口
题目
- 给定一个含有 n 个正整数的数组和一个正整数 target 。
- 找出该数组中满足其和 ≥ target 的长度最小的 连续子数组
[numsl, numsl+1, ..., numsr-1, numsr],并返回其长度。如果不存在符合条件的子数组,返回0
示例
输入: target = 7, nums = [2,3,1,2,4,3]
输出: 2
解释: 子数组 [4,3] 是该条件下的长度最小的子数组。
输入: target = 4, nums = [1,4,4]
输出: 1
输入: target = 11, nums = [1,1,1,1,1,1,1,1]
输出: 0
解法
-
定义两个指针
start和end分别表示子数组(滑动窗口窗口)的开始位置和结束位置,维护变量ans存储子数组中的元素和(即从nums[start]到nums[end]的元素和)。 -
初始状态下,
start和end都指向下标0,ans的值为0。 -
每一轮迭代,将
nums[end]加到ans,如果ans ≥ target,则更新子数组的最小长度(此时子数组的长度是end − start + 1),然后将nums[start]从ans中减去并将start右移,直到ans < target,在此过程中同样更新子数组的最小长度。在每一轮迭代的最后,将end右移。
/**
* @param {number} target
* @param {number[]} nums
* @return {number}
*/
var minSubArrayLen = function(target, nums) {
let ans = 0;
nums.forEach(ele => ans += ele);
// 处理两种特殊情况
if (ans < target) {
return 0;
}
if (ans == target) {
return nums.length;
}
// 当ans大于target的情况
let start = 0;
let end = 0;
ans = 0;
let minLen = Infinity;
let len = 1;
// 标志位
let flag = false;
while (start >= 0 && start < nums.length && end >= 0 && end < nums.length) {
if(!flag){
ans += nums[end];
}
if (ans < target) {
flag = false;
end++;
}else {
flag = true;
// 记录下当前符合的长度
len = end - start + 1;
// 取得当前最小的长度
minLen = Math.min(minLen,len);
if (start == end) {
end++;
}
ans -= nums[start];
start++;
}
}
return minLen;
};
- 简洁的解法
/**
* @param {number} target
* @param {number[]} nums
* @return {number}
*/
var minSubArrayLen = function(target, nums) {
let ans = 0;
// 处理两种特殊情况
if (ans < target) {
return 0;
}
// 使用滑动窗口算法
ans = 0;
let start = 0;
let end = 0;
let n = nums.length;
let minLen = Infinity;
while (end < n){
ans += nums[end];
while(ans >= target) {
minLen = Math.min(minLen, end - start + 1);
ans -= nums[start];
start++;
}
end++;
}
return minLen;
};
附录
- 双指针思想