• [LeetCode] 862. Shortest Subarray with Sum at Least K


    Given an integer array nums and an integer k, return the length of the shortest non-empty subarray of nums with a sum of at least k. If there is no such subarray, return -1.

    A subarray is a contiguous part of an array. 

    Example 1:

    Input: nums = [1], k = 1
    Output: 1
    

    Example 2:

    Input: nums = [1,2], k = 4
    Output: -1
    

    Example 3:

    Input: nums = [2,-1,2], k = 3
    Output: 3

    Constraints:

    • 1 <= nums.length <= 105
    • -105 <= nums[i] <= 105
    • 1 <= k <= 109

    和至少为 K 的最短子数组。

    给你一个整数数组 nums 和一个整数 k ,找出 nums 中和至少为 k 的 最短非空子数组 ,并返回该子数组的长度。如果不存在这样的 子数组 ,返回 -1 。

    子数组 是数组中 连续 的一部分。

    来源:力扣(LeetCode)
    链接:https://leetcode.cn/problems/shortest-subarray-with-sum-at-least-k
    著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

    这道题很好,我直接介绍最优解,思路是前缀和 + 单调队列。本题同 LintCode 1507题

    注意题目让你找的是子数组,同时要知道子数组的数组和,那么第一反应就应该是先用前缀和去记录整个数组的前缀和,然后再找机会去找到满足题意的子数组。同时这里我们注意题目给的条件,1 <= k <= 109,说明 K 是一个正数,又由于我们找的是前缀和数组里面一段 sum = K 的子数组,那么 sum 也必须大于 0 。这一段前缀和子数组最好是首个元素 presum[first] 尽可能小 && 最后一个元素 presum[last] 尽可能大。由这个推断出的条件我们可以尝试使用单调队列,并且这是一个单调递增队列。这里如果想不清楚你可以举个反例,单调递减的队列肯定不对,因为如果满足题意的子数组这一段的前缀和是单调递减的,那么说明这一段子数组的和一定是负的,就会和 K > 0 冲突。同时注意单调栈/单调队列的题,我们放入栈内的元素一般都是数组的下标而不是值,起码 leetcode 上的题都是这样。

    所以这里我们先开一个数组记录 input 数组的前缀和,记为 int[] presum。接着我们开始遍历这个 presum 数组。对于每个遇到的 presum[i],如果当前队列不为空,我们去看一下当前这个 index i 的前缀和 presum[i] 是否 <= 此时队列最右边元素指向的前缀和。这里我们是为单调递增的那一段找一个可能的最低点。如果一直满足这个条件,presum[i] <= presum[queue.peekLast()],我们就把 queue 最右边那个index 一直 remove。这样当操作停止的时候,queue 最右边那个 index 指向的前缀和是最小的。

    接下来我们找一个可能的最高点。对于当前 index 指向的前缀和 presum[i],我们去看一下队列最左边那个 index 指向的前缀和是否满足 presum[queue.peekFirst()] + k >= presum[i]。如果满足这个公式,则我们就找到了一个可行解,这个解的长度是 i - queue.removeFirst()。此时就可以把队列最左边那个 index 去掉了,因为已经检查过了。

    时间O(n^2) - worst case

    空间O(n) - 前缀和数组

    Java实现

     1 class Solution {
     2     public int shortestSubarray(int[] nums, int k) {
     3         int len = nums.length;
     4         long[] presum = new long[len + 1];
     5         for (int i = 0; i < len; i++) {
     6             presum[i + 1] = presum[i] + (long) nums[i];
     7         }
     8         
     9         int res = Integer.MAX_VALUE;
    10         Deque<Integer> queue = new LinkedList<>();
    11         for (int i = 0; i < presum.length; i++) {
    12             // find a lower point if possible
    13             while (!queue.isEmpty() && presum[i] <= presum[queue.peekLast()]) {
    14                 queue.removeLast();
    15             }
    16             // check if the current number >= the left most in queue + k
    17             while (!queue.isEmpty() && presum[i] >= presum[queue.peekFirst()] + k) {
    18                 res = Math.min(res, i - queue.removeFirst());
    19             }
    20             queue.addLast(i);
    21         }
    22         return res == Integer.MAX_VALUE ? -1 : res;
    23     }
    24 }

    LeetCode 题目总结

  • 相关阅读:
    第十三周助教作业
    【西北师大-19软工】第五次作业成绩汇总
    第十二周助教工作总结
    第十一周助教工作总结
    第九周助教工作总结
    第八周助教工作总结
    第七周助教工作总结
    SQLAlchemy 学习笔记(二):ORM 基础
    WebSocket 与 HTTP/2
    Chrome 与 Firefox-Dev 的 DevTools
  • 原文地址:https://www.cnblogs.com/cnoodle/p/16404877.html
Copyright © 2020-2023  润新知