LeetCode探索(162):862-和至少为 K 的最短子数组

251 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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^5
  • 1 <= 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)。

参考