JAVA程序设计:和至少为 K 的最短子数组(LeetCode:862)

返回 A 的最短的非空连续子数组的长度,该子数组的和至少为 K 。

如果没有和至少为 K 的非空子数组,返回 -1 。

 

示例 1:

输入:A = [1], K = 1
输出:1
示例 2:

输入:A = [1,2], K = 4
输出:-1
示例 3:

输入:A = [2,-1,2], K = 3
输出:3
 

提示:

1 <= A.length <= 50000
-10 ^ 5 <= A[i] <= 10 ^ 5
1 <= K <= 10 ^ 9

方法一:优先队列。(自己的方法)

       我们使用优先队列存储前缀和以及对应的索引,并按照升序排序,用遍历到当前位置的前缀和与队列的首元素进行比较,若差值大于等于K,则说明这一段子数组是符合要求的,我们更新答案后弹出首元素并继续比较,若小于K,则说明当前位置之前剩下所能构成的连续子数组的和不会大于等于K,因此加入当前值并继续往下遍历。

class Solution {
	
	class node implements Comparable<node>{
		long val;
		int index;
		public node(long val,int index) {
			this.val=val;
			this.index=index;
		}
		@Override
		public int compareTo(node o) {
			// TODO 自动生成的方法存根
			if(val==o.val)
				return o.index-index;
			return val<o.val?-1:1;
		}
	}
	
    public int shortestSubarray(int[] A, int K) {
    	
    	int n=A.length;
    	long[] sum=new long[n];
    	PriorityQueue<node> q=new PriorityQueue<>();
    	
    	int ans=n+1; 
    	sum[0]=A[0];
    	q.add(new node(sum[0],0));
    	
    	if(sum[0]>=K) return 1;
    	
    	for(int i=1;i<n;i++) {
    		sum[i]=sum[i-1]+A[i];
    		while(!q.isEmpty() && sum[i]-q.peek().val>=K)
    			ans=Math.min(ans, i-q.poll().index);
    		if(sum[i]>=K) ans=Math.min(ans, i+1);
    		q.add(new node(sum[i],i));
    	}
    	
    	return ans>n?-1:ans;
    	
    } 
}

方法二:滑动窗口。(参考官方题解

首先我们预处理出前缀和sun数组,而我们的目标是找到x和y使得sum[y]-sum[x]>=K并且y-x尽可能的小,我们令f(y)表示对于固定的y,最大的满足sum[x]<=sum[y]-K的x,这样所有y-f(y)中的最小值即为答案,我们可以从中发现两条性质:

(1)假设两个索引x1和x2,其中x1<x2,满足sum[x2]<=sum[x1],那么f(y)的值一定不会为x1,因为x2比x1大,并且如果x1满足了sum[x1]<=f(y)-K。那么sum[x2]<=sum[x1]<=sum[y]-K,即x2同样满足sum[x2]<=sum[y]-K。

(2)若f(y1)的值为x,那么之后就没必要再考虑x了,因为如果有y2>y1且f(y2)的值也为x,但此时y2-x显然大于y1-x,不会作为所有y-f(y)中的最小值。

我们维护一个前缀和数组sum的单调队列,它是一个双端队列,其中存放的元素为下标x0,x1,x2...满足sum[x0],sum[x1]...单调递增,我们可以通过在队尾移除若干元素使得队列单调递增。这是为了满足性质(1)。

同时,我们也会在队首移除若干元素,如果sum[y]>=sum[x0]+K,则将队首元素移除,直到该不等式不满足,这是为了满足性质(2)。

class Solution {
    public int shortestSubarray(int[] A, int K) {
    	
    	int n=A.length;
    	long[] sum=new long[n+1];
    	Deque<Integer> q=new LinkedList<>();
    	
    	int ans=n+1;
    	for(int i=0;i<n;i++)
    		sum[i+1]=sum[i]+A[i];
    	
    	for(int i=0;i<sum.length;i++) {
    		while(!q.isEmpty() && sum[i]<=sum[q.getLast()])
    			q.removeLast();
    		while(!q.isEmpty() && sum[i]>=sum[q.getFirst()]+K)
    			ans=Math.min(ans, i-q.removeFirst());
    		q.addLast(i);
    	}
    	
    	return ans>n?-1:ans;
    	
    } 
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值