持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第27天,点击查看活动详情
题目
给你一个整数数组 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 <= 10^5-10^5 <= nums[i] <= 10^51 <= k <= 10^9
思考
本题难度困难。
首先是读懂题意。给你一个整数数组 nums 和一个整数 k ,找出 nums 中和至少为 k 的最短非空子数组,并返回该子数组的长度。如果不存在这样的子数组,返回 -1 。
首先,计算数组的前缀和数组 sum,新建单调递增队列 myque 用于存储索引。
遍历 sum 数组,如果队列长度 > 0 且新加入的数比队列尾的数小,那么队列尾的数就可以丢去,这样每次加进去的元素都会是 myque 中的唯一最大值,使得 myque 中的元素是按照添加顺序严格单调递增的。
此外,如果队列长度 > 0 且一个数和队列头的数已经满足了条件,那么队列头的数就可以丢去,因为后续即使还有以它为起点的满足条件的子数组,长度也会大于当前的长度。
解答
方法一:前缀和数组 + 单调双端队列
/**
* @param {number[]} nums
* @param {number} k
* @return {number}
*/
var shortestSubarray = function(nums, k) {
const n = nums.length
let ans = n + 1 // ans不会超过n+1
// 前缀和数组
const sum = [0]
for(let i = 1; i <= n; i++){
sum[i] = sum[i-1] + nums[i-1]
}
// 单调递增队列,存储索引
const myque = []
for(let i = 0; i < sum.length; i++){
while(myque.length > 0 && sum[myque[myque.length - 1]] >= sum[i]){
myque.pop()
}
while(myque.length > 0 && sum[i] - sum[myque[0]] >= k){
ans = Math.min(ans, i - myque.shift())
}
myque.push(i)
}
return ans < n + 1 ? ans : -1
}
复杂度分析:
- 时间复杂度:O(n),其中 n 是数组 nums 的长度。求前缀和数组 sum 消耗 O(n),sum 每个下标会入队列 myque 一次,最多出队列 myque 一次。
- 空间复杂度:O(n)。前缀和数组 sum 和 队列 myque 的长度均为 O(n)。