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

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

思路:题目要求找到一个最短的子数组,使得子数组的和大于等于 k。不难想到,可以使用前缀和快速计算子数组的和。

我们用一个长度为 n+1 的数组 s[i] 表示数组 nums 前 i 个元素的和。另外,我们需要维护一个严格单调递增的队列 q,队列中存储的是前缀和数组 s[i] 的下标。注意,这里的单调递增是指下标对应的前缀和的大小,而不是下标的大小。

为什么存的是下标呢?这是为了方便计算子数组的长度。那为什么队列严格单调递增?我们可以用反证法来说明。

假设队列元素非严格单调递增,也即是说,存在下标 i 和 j,满足 i<j,且 s[i]≥s[j]。

当遍历到下标 k,其中 i<j<k≤n,此时 s[k]−s[j]≥s[k]−s[i],且 nums[j…k−1] 的长度小于 nums[i…k−1] 的长度。由于下标 jjj 的存在,子数组 nums[i…k−1] 一定不是最优解,队列中的下标 iii 是不必要的,需要将其移除。因此,队列中的元素一定严格单调递增。

回到这道题目上,我们遍历前缀和数组 s,对于遍历到的下标 i,如果 s[i]−s[q.front]≥k,说明当前遇到了一个可行解,我们可以更新答案。此时,我们需要将队首元素出队,直到队列为空或者 s[i]−s[q.front]<k为止。

如果此时队列不为空,为了维持队列的严格单调递增,我们还需要判断队尾元素是否需要出队,如果 s[q.back]≥s[i],则需要循环将队尾元素出队,直到队列为空或者 s[q.back]<s[i] 为止。然后,我们将下标 i 入队。

遍历结束,如果我们没有找到可行解,那么返回 −1。否则,返回答案。

代码:

class Solution {
    public int shortestSubarray(int[] nums, int k) {
        int n = nums.length;
        long[] preSumArr = new long[n + 1];
        for (int i = 0; i < n; i++) {
            preSumArr[i + 1] = preSumArr[i] + nums[i];
        }
        int res = n + 1;
        Deque<Integer> queue = new ArrayDeque<Integer>();
        for (int i = 0; i <= n; i++) {
            long curSum = preSumArr[i];
            while (!queue.isEmpty() && curSum - preSumArr[queue.peekFirst()] >= k) {
                res = Math.min(res, i - queue.pollFirst());
            }
            while (!queue.isEmpty() && preSumArr[queue.peekLast()] >= curSum) {
                queue.pollLast();
            }
            queue.offerLast(i);
        }
        return res < n + 1 ? res : -1;
    }
}

执行结果:
Alt
Alt

Alt
总结: 通过每日一题来锻炼自己,提升自己的能力,开拓自己的思维,每天收获一点点,对自己有很大帮助。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值