LeetCode 862. 和至少为 K 的最短子数组

LeetCode 862. 和至少为 K 的最短子数组

返回 A 的最短的非空连续子数组的长度,该子数组的和至少为 K
如果没有和至少为 K 的非空子数组,返回 -1

思路

初看这道题类似最大子数组和,本来打算用滑动窗口解决,但是超时。
看完题解可以使用 前缀和数组递增队列 解。
官方的题解不是很清楚,这里试着详细解释一下。

前缀和数组

P[i]表示数组前i个元素的和。那么本题就可以转化为
y > x P [ y ] − P [ x ] > = K (1) y > x \\ P[y] - P[x] >= K \tag{1} y>xP[y]P[x]>=K(1)
o b j [ y ] = x obj[y] = x obj[y]=x表示对于 y y y来说 最大的满足 公式(1)的 x x x
这里有一个推论:

如果有 x 1 < x 2 x1<x2 x1<x2,但是 P [ x 1 ] > P [ x 2 ] P[x1]>P[x2] P[x1]>P[x2]。那么对于固定的 y y y来说, o b j [ y ] = x 2 obj[y] = x2 obj[y]=x2。因为此时如果 P [ y ] − P [ x 1 ] > = K P[y] - P[x1] >= K P[y]P[x1]>=K那么 P [ y ] − P [ x 2 ] P[y] - P[x2] P[y]P[x2]肯定大于 K K K,且 y − x 2 < y − x 1 y-x2 < y-x1 yx2<yx1

所以对于固定的 y y y来说,找 o b j [ y ] = x obj[y] = x obj[y]=x的过程只需要考虑那些满足 P [ x ] P[x] P[x]递增的x。所以可以构造一个递增队列来求解。

下面更具代码来解释,因为go构造队列太麻烦了,本文使用双指针数组实现双端队列。

代码

func shortestSubarray(A []int, K int) int {
    result := len(A) + 1

    P := make([]int, len(A)+1, len(A)+1)  //前缀和数组,P[i]表示数组前i个元素的和。
    IncreaQ := make([]int, len(A)+1, len(A)+1) //递增队列
    P[0] = 0 //在最前面加一项0,前0个元素的和是0
    IncreaQ[0] = 0

    for i:=0;i<len(A);i++ { //构造前缀和数组
        P[i+1] = P[i] + A[i]
    }

    leftL := 0 //双指针实现双端队列
    rightL := 0
    for i:=1;i<=len(A);i++ { // 遍历P数组
        for rightL >= leftL { //删除队尾
        // 从递增队列IncreaQ中删除队尾的大于P[i]的项。因为,如果过那些项满足条件的话,P[i]更满足了。此时i是更优解。
            if(P[i] < P[IncreaQ[rightL]]) {
                rightL--
            } else {
                break
            }
        }
        rightL++
        IncreaQ[rightL] = i

        for leftL <=rightL {
        // 从前开始删除所有满足 P[i] - P[x] >= K 的元素。
        // 因为,对于i之后的P[]来说,如果这些元素也满足公式(1)的话。
        // 他们的结果肯定比i大,所以对于i以后的解来说,不用考虑这些被删掉的。
            if( P[i] - P[IncreaQ[leftL]] >= K ) {
                if( i - IncreaQ[leftL] < result ) {
                    result = i - IncreaQ[leftL]
                }
                leftL++        
            } else {
                break
            }
        }
    } 
// 边构造边遍历递增队列,构造完就遍历完了。
    if result == len(A) + 1 {
        return -1
    }
    return result


}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值