【LeetCode】209. 長度最小的子陣列

Curryxin發表於2021-08-04

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);

體會

連續子陣列+和 --> 字首和

相關連結

長度最小的子陣列-看滑動視窗
長度最小的子陣列-看字首和

相關文章