LeetCode 每日一题:和至少为 K 的最短子数组

87 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第 16 天,点击查看活动详情

和至少为 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 <=10510^5
  • 105-10^5<= nums[i] <=10510^5
  • 1 <= k <=10910^9

思路分析

  1. 题目中要求子数组的和不能小于 k,并且是连续的;
  2. 我们知道对于一个含有非负数的数组来说,记 sumisum_i 为前i项的和,那么按照前面的条件来说,一定会存在 sumi+1sumisum_{i+1} ≥ sum_i,那么题目可以转换为需要求 sumjsumiksum_j - sum_i ≥ k,且 j-i 最小的值;
  3. 使用数组来存储滑动窗口的值,每次循环时在数组中添加此次循环的下标,按照 res.length !== 0 && arr[j] - arr[res[0]] >= k 的条件来确定新一轮的滑动窗口的初始值;
  4. 等到循环遍历结束,判断得到的 min 值「因为初值为 nums.length + 1」,若小于 nums.length+1 则说明存在这样的子数组,否则不存在返回-1。

AC 代码

/**
 * @param {number[]} nums
 * @param {number} k
 * @return {number}
 */
var shortestSubarray = function(nums, k) {
    let arr = new Array(nums.length + 1).fill(0)
    for(let i=0; i < nums.length; i++){
        arr[i + 1] = arr[i] + nums[i]
    }
    let res = [], min = nums.length + 1
    for(let j = 0; j < arr.length; j++){
        // 上次的和大于本次,即sum[j-1]>arr[j],则不存取
        while(res.length !== 0 && arr[res[res.length - 1]] >= arr[j]){
            res.pop()
        }
        while(res.length !== 0 && arr[j] - arr[res[0]] >= k){
            // 当本次的sum[j]>arr,则寻找获取最小长度
            min = Math.min(j - res[0], min)
            // 删除滑动窗口初始值,滑动窗口上次结束值为初始值,当前j为滑动窗口结束值
            res.shift()
        }
        res.push(j)
    }
    return min < nums.length + 1 ? min : -1
};

结果:

  • 执行结果: 通过
  • 执行用时:2468 ms, 在所有 JavaScript 提交中击败了19.44%的用户
  • 内存消耗:55.8 MB, 在所有 JavaScript 提交中击败了58.33%的用户
  • 通过测试用例:97 / 97

END