题目描述
给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的连续子数组,并返回其长度。如果不存在符合条件的连续子数组,返回 0。
思考
由题目可知, 我们需要找的是长度最小的连续子数组,看到这类连续子集问题,一般可以尝试着使用栈或者队列来做。
这道题我们可以维护一个队列,每当有新数组元素的时候,我们将其压入队列,并计算队列所有数字的总和sum
,如果sum
大于目标值,我们就与之前暂存的最小长度比较,如果小于最小长度,那就更新最小长度, 然后将队头元素出队列。
let minL = 0, minR = nums.length + 1;
let i = minL, j = minL; // i j 表示队列头尾指针
let sum = nums[0];
while(true) {
if (sum >= s) {
if ( j - i < minR - minL) {
minL = i;
minR = j;
}
sum = sum - nums[i];
i++;
} else {
j++;
sum = sum + nums[j];
}
if (i >= nums.length) {
break;
}
}
return minR -minL + 1;
这里需要注意的一点是 有以下特殊情况
s = 7 nums = [1, 1, 1, 7];
如上面例子的数组, 我们在将数字7
压入队列的时候,sum
是大于7
的, 这时候最小长度是4
。但是这并非是最小长度,我们可以发现的是,将队头元素出队列之后,sum
还是大于7
。 因此,每当我们得到一个大于s
的sum
值的时候, 我们在比较完最小长度之后,还需要确定出对列之后的sum
值是否仍是大于s
的。
接下来我们需要考虑一下特殊情况
1 nums
的和小于s
, 因此我们需要一个字段标识是否找了符合要求的序列, 如果有,计算子序列长度,如果没有 则返回0
2 当队尾指针j
指向了nums
尾部的时候, 如果接下来队列的数值和小于s
,则我们不需要再移动队头指针i
, 因为队列的元素总和小于s
, 队列长度不会增加,只会减少。
代码改造如下
....
let hasMin = false;
while(true) {
// console.log(i, j, sum);
if (sum >= s) {
hasMin = true;
....
} else if (j >= nums.length - 1) {
break;
} else {
....
}
if (i >= nums.length) {
break;
}
}
// console.warn(minL, minR, sum);
return hasMin ? minR - minL + 1 : 0;
完整代码
var minSubArrayLen = function(s, nums) {
if (nums.length < 1) {
return 0;
}
let minL = 0, minR = nums.length + 1;
let i = minL, j = minL;
let sum = nums[0];
let hasMin = false;
while(true) {
if (sum >= s) {
hasMin = true;
if ( j - i < minR - minL) {
minL = i;
minR = j;
}
sum = sum - nums[i];
i++;
} else if (j >= nums.length - 1) {
break;
} else {
j++;
sum = sum + nums[j];
}
if (i >= nums.length) {
break;
}
}
return hasMin ? minR - minL + 1 : 0;
};