862. 和至少為 K 的最短子陣列
知識點:單調;佇列;字首和
題目描述
返回 A 的最短的非空連續子陣列的長度,該子陣列的和至少為 K 。
如果沒有和至少為 K 的非空子陣列,返回 -1 。
示例
輸入:A = [1], K = 1
輸出:1
輸入:A = [1,2], K = 4
輸出:-1
輸入:A = [2,-1,2], K = 3
輸出:3
解法一:單調佇列+字首和
這個題目是求連續子陣列,所以自然可以想到字首和,也就是用一個數組統計到第i個位置的字首和。所以問題就變成了
j>i && preSum[j] - preSum[i] >= k && (j-i) 最小
可以通俗的理解,就像排隊,我需要找到前面比我矮最少k的人,而且想讓我和這個人的距離最近;
雙端遞增佇列:對於當前的“我”來說,if前面的人比我高,那我的身高減去前面高的人,值肯定為負數,那就可以直接彈走了,(那可能會有個問題,這就直接彈走了?那要是之後來的要是比上一個更小,比之前彈出的更大那咋整,你想想,這不正好嗎?首先剛進去的這個肯定更小,if彈走的滿足,那這個更滿足了,其次,這個離得也更近啊,所以可以大膽的彈),那現在佇列裡剩下的都是比我矮的人了,if第一個和我的身高差值小於k,那後面就更小於k了;if隊首和我的身高相比差值大於k,那就可以去當做一個答案記錄了,並且可以把這個值彈出,然後比較新的隊首了,(為什麼可以彈走呢?因為後來的無論和隊首比滿不滿足,那都沒用,因為肯定我離之前那個同學更近啊);
class Solution {
public int shortestSubarray(int[] nums, int k) {
int n=nums.length,res=n+1;
//雙端佇列;
LinkedList<Integer> list=new LinkedList<>();
int[] pre=new int[n+1];
for(int i=1;i<=n;i++)
//得到字首和;
pre[i]=pre[i-1]+nums[i-1];
for(int i=0;i<n+1;i++)
{
//保持佇列單調;
while(!list.isEmpty()&&pre[i]<pre[list.getLast()])
list.removeLast();
//彈出滿足了的隊首,逐步找到最小的;
while(!list.isEmpty()&&pre[i]-pre[list.getFirst()]>=k)
{
res=Math.min(res,i-list.removeFirst());
}
list.add(i);
}
return res==n+1?-1:res;
}
}