209. 長度最小的子陣列
知識點:陣列;字首和;二分法;雙指標;滑動視窗
題目描述
給定一個含有 n 個正整數的陣列和一個正整數 target 。
找出該陣列中滿足其和 ≥ target 的長度最小的 連續子陣列 [numsl, numsl+1, ..., numsr-1, numsr] ,並返回其長度。如果不存在符合條件的子陣列,返回 0 。
示例
輸入:target = 7, nums = [2,3,1,2,4,3]
輸出:2
解釋:子陣列 [4,3] 是該條件下的長度最小的子陣列。
輸入:target = 4, nums = [1,4,4]
輸出:1
輸入:target = 11, nums = [1,1,1,1,1,1,1,1]
輸出:0
解法一:暴力法
使用兩層for迴圈遍歷;
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int ans = Integer.MAX_VALUE;
for(int i = 0; i < nums.length; i++){
int sum = 0;
for(int j = i; j < nums.length; j++){
sum += nums[j];
if(sum >= target){
ans = Math.min(ans, j-i+1);
break;
}
}
}
return ans == Integer.MAX_VALUE ? 0 : ans;
}
}
時間複雜度:0(N^2);
解法二:字首和+二分法
這道題一看連續子陣列觸發了字首和,字首和就是用來解決連續子陣列問題的,所以可以先統計到達每個的字首和,仍然是為了方便,建立長度是n+1的陣列,第一個位置放0,這樣最後輸出子陣列長度的時候不用再去+1了,直接j-i就可以了。
字首和之後,要想尋找子陣列和大於target,就是找sum[i]-sum[j] >= target, 仍然需要遍歷兩遍,但是字首和陣列有一個重要的特點,就是是遞增的(因為只有正數),所以這時候可以用二分搜尋,將複雜度降低到O(logN),我們就是想找大於等於sum[i]+target的值.
在java中Arrays類有可以直接呼叫的Arrays.binarySearch(陣列,目標值);如果能找到,就返回找到值的下標,如果沒找到就返回一個負數,這個負數取反之後就是查詢的值應該在原陣列中的位置。
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int n = nums.length;
int ans = Integer.MAX_VALUE;
int[] prenum = new int[n+1];
for(int i = 1; i <= n; i++){
prenum[i] = prenum[i-1] + nums[i-1];
}
for(int i = 0; i < n; i++){
int t = prenum[i] + target;
int index = Arrays.binarySearch(prenum, t);
if(index < 0) index = ~index; //負數的話取反是其應該在元素中的位置;其實也就是第一個比它大的值的元素;
if(index <= n){
ans = Math.min(ans, index-i);
}
}
return ans == Integer.MAX_VALUE ? 0 : ans;
}
}
時間複雜度:0(NlogN);
解法三:滑動視窗
滑動視窗其實就是雙指標的一種,只不過在移動過程中像是一個視窗在移動,所以稱作滑動視窗,這是解決連續陣列中一個很常見的思路;
可以定義兩個指標start和end(分別表示視窗的開始和結束),end從頭到尾去遍歷陣列,當移動出去之後遍歷結束。在過程中,將nums[end]不斷的加到sum上,如果sum>= target, 那就更新陣列的最小長度,然後將nums[stat]在sum中減去,start前移,直到sum< target後,end再移;
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int ans = Integer.MAX_VALUE;
int start = 0, end = 0;
int sum = 0;
while(end < nums.length){
sum += nums[end];
while(sum >= target){
ans = Math.min(ans, end-start+1);
sum -= nums[start]; //超過之後把前面的減去,視窗移動;
start++;
}
end++;
}
return ans == Integer.MAX_VALUE ? 0 : ans;
}
}
時間複雜度:0(NlogN);
體會
連續子陣列+和 --> 字首和