1 題目描述
給定一個含有 n 個正整數的數組和一個正整數 s ,找出該數組中滿足其和 ≥ s 的長度最小的連續子數組。如果不存在符合條件的連續子數組,返回 0。
示例:
輸入: s = 7, nums = [2,3,1,2,4,3]
輸出: 2
解釋: 子數組 [4,3] 是該條件下的長度最小的連續子數組。
進階:
如果你已經完成了O(n) 時間複雜度的解法, 請嘗試 O(n log n) 時間複雜度的解法。
2 解法
在做862時無意中做出了這個題。它和862的區別是,數組中的數都是正數。
在O(N)時間內計算nums的累積和數組sums,sums[i]=nums[0]+nums[1]+…+nums[i]。
假設當前窗口爲k,在O(n)時間內可以計算出所有窗口大小爲k的子數組的和,如果最大的比s小,要增大窗口,如果最大的比s大(或相等)就縮小窗口。這樣最多lg(n)次循環就可以了。
3 java源碼
class Solution {
// A的累計和數組,sums[i] = sums(A[0]+A[1]+...+A[i])
int[] sums;
int K;
int[] A;
int minWindownSize;
/**
* 窗口大小固定時,計算每個窗口內所有值的和,返回最大的和
* 時間複雜度爲O(n)
*
* @param x 窗口大小
* @return 最大的窗口內值的和
*/
private int maxSumOverFixedSizeWindow(int x) {
int max = sums[x-1];
for (int i = 1; i < sums.length-x+1; i++) {
int sum = sums[i+x-1]-sums[i-1];
if (sum > max) max = sum;
}
return max;
}
/**
* 可能的窗口範圍在[min, max]之間,確定一箇中間的數,然後遞歸查找
* @param min
* @param max
* @return
*/
private void find(int min, int max) {
if (min > max) return;
int x = (max-min)/2+min; // 窗口大小
if (minWindownSize > 0 && minWindownSize <= x) return;
int maxSum = maxSumOverFixedSizeWindow(x); // 窗口大小爲x時,窗口內值的和最大爲maxSum
if (maxSum < K) { // 擴大窗口,沒有找到,擴大窗口,看看能不能找到,可能會一直擴大到窗口爲n,
find(x+1, max);
find(min, x-1);
} else { // 縮小窗口 maxSum >= K,找到了,繼續尋找有沒有更好的,要一直縮小到窗口爲1
minWindownSize = x;
find(min, x-1);
}
}
public int minSubArrayLen(int s, int[] nums) {
int n = nums.length;
if (n == 0) return 0;
sums = new int[n];
sums[0] = nums[0];
for (int i = 1; i < n; i++) {
sums[i] = sums[i-1]+nums[i];
}
this.K = s;
this.A = nums;
this.minWindownSize = 0;
for (int size = 1; size <= n; size*=2) {
int maxSum = maxSumOverFixedSizeWindow(size);
if (maxSum >= K) {
minWindownSize = size;
}
}
if (minWindownSize != 0) {
find(1, minWindownSize);
} else {
find(1, n);
}
return this.minWindownSize;
}
}