【Leetcode】862.和至少为 K 的最短子数组(前缀和)

leetcode-862.png

前缀和问题~
首先用暴力来破解

class Solution {
    public int shortestSubarray(int[] nums, int k) {
        int length = nums.length;
        if(length == 1){
            if(nums[0] == k){
                return 1;
            }
        }
        int[] pres = new int[length+1];
        pres[0] = 0;
        for(int i = 1; i <= length; ++i){
            pres[i] = pres[i-1] + nums[i-1];
        }
        int minLen = Integer.MAX_VALUE;
        for(int i = 0; i <= length; ++i){
            for(int j = i; j <= length; ++j){
                if(pres[j] - pres[i] >= k){
                    minLen = Math.min(minLen, j - i);
                }
            }
        }
        return minLen == Integer.MAX_VALUE ? -1 : minLen;
    }
}

一般来说,思路简单,能暴力破解的,基本超时


超时.png

简单的思考一个问题,如果要达到最短的距离,那么这个前缀和的子数组肯定是一个单调递增的数组,一定一定是单调的
对于pres[i,j] >= k 成立
那么此时有一个 pres[i+1] < pres[i],那么肯定有 pres[i+1,j] <= k
且此时的距离更短

所以我们的目的就是要维护一个单调递增的前缀和数组
用队列这种数据结构就很适合
pres[i] <= 队列尾部的数的时候,将队尾数字移除
pres[i] - 队列首部元素 >= k, 更新最短距离,并将队列首部元素移除

class Solution {
    public int shortestSubarray(int[] nums, int k) {
        int length = nums.length;
        long[] pres = new long[length+1];  // 使用int时,不会ac
        for(int i = 0; i < length; ++i){
            pres[i+1] = pres[i] + nums[i];
            if(nums[i] >= k)
                return 1;
        }
        int minLen = Integer.MAX_VALUE;
        Deque queue = new ArrayDeque<>();
        for(int i = 0; i <= length; ++i){
            while(!queue.isEmpty() && pres[i] <= pres[queue.getLast()])
                queue.removeLast();
            while(!queue.isEmpty() && pres[i] - pres[queue.peek()] >= k)
                minLen = Math.min(minLen, i - queue.poll());
            queue.add(i);
        }
        return minLen == Integer.MAX_VALUE ? -1 : minLen;
    }
}

你可能感兴趣的:(【Leetcode】862.和至少为 K 的最短子数组(前缀和))