持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第26天,点击查看活动详情
862. 和至少为 K 的最短子数组
给你一个整数数组 nums 和一个整数 k ,找出 nums 中和至少为 k 的 最短非空子数组 ,并返回该子数组的长度。如果不存在这样的 子数组 ,返回 -1 。
子数组 是数组中 连续 的一部分。
示例 1:
输入: nums = [1], k = 1
输出: 1
示例 2:
输入: nums = [1,2], k = 4
输出: -1
示例 3:
输入: nums = [2,-1,2], k = 3
输出: 3
提示:
1 <= nums.length <= 105-105 <= nums[i] <= 1051 <= k <= 109
暴力解法优化:剪枝
- 在两层for循环的基础上进行剪枝。
- 由于k>=1,所以任何连续子数组内一旦第一个数为负数,说明后面还需要一个正数对数组和进行调节。导致该数组不是最短的。
- if(第一个数值为非正数,则舍弃该遍历,不为它找子数组求和。)
- 在求和过程中,如果前缀和为非正数
- 将它和第一个数值同样处理,舍弃后面的遍历,不再为它找子数组求和。
/**
* @param {number[]} nums
* @param {number} k
* @return {number}
*/
var shortestSubarray = function(nums, k) {
let count = nums.length;
let min = count + 1;
for (let i = 0; i < count; i++) {
if (nums[i] <= 0) {
continue;
}
if (nums[i] >= k) {
return 1;
}
let total = nums[i];
for (let j = i + 1; j < count && (j - i + 1) < min; j++) {
total += nums[j];
if (total <= 0) {
break;
}
if (total >= k) {
if (j - i + 1 < min) {
min = j - i + 1;
}
}
}
}
if (min === count + 1) {
return -1;
}
return min;
};
前缀和+单调队列
- 要获取前缀和 >= K, 则队列里的前缀和必须是递增的 否则两个前缀和的差值必为负数,而K是 >=1 的
- 出现满足 >= k 的关系式子 则开始取 区间更接近 也就是说子数组长度更小的结果
- 此时最左边的元素可以移掉了 原因是 下一个元素即使满足 差值 >= K 但是他和下一个元素
- 之间的距离一定会比当前元素的距离要大。
var shortestSubarray = function(nums, k) {
const n = nums.length;
let sum:number[] = new Array(n+1);
sum[0] = 0;
for(let i=1; i<=n; i++){
sum[i] = sum[i-1] + nums[i-1];
}
let q:number[] = [];
let l = 0;
let res = Infinity;
for(let i=0; i<=n; i++){
while(l<q.length && sum[i]-sum[q[l]]>=k){
res = Math.min(res, i-q[l]);
l++;
}
// maintain monotonic incremental stack
while(q.length && sum[i]<sum[q[q.length-1]]){
q.pop();
}
q.push(i);
}
return res === Infinity ? -1 : res;
};