LeetCode C++ 209. Minimum Size Subarray Sum【二分/滑窗/雙指針】中等

Given an array of n positive integers and a positive integer s, find the minimal length of a contiguous subarray of which the sum ≥ s. If there isn’t one, return 0 instead.

Example:

Input: s = 7, nums = [2,3,1,2,4,3]
Output: 2
Explanation: the subarray [4,3] has the minimal length under the problem constraint.

Follow up: If you have figured out the O(n) solution, try coding another solution of which the time complexity is O(nlogn).

題意:給出一個由正整數組成的長爲 n 的區間,找到最短的連續區間——其區間和滿足 >= s 的條件。

思路1:和之前做過的“紅魔館爆炸了”幾乎一樣,所以一上手就這麼寫了。通過二分搜索長度,然後用固定長度的滑動窗口搜索數組,得到最大的區間和,如果可以滿足 >=s 的條件,則 hi = mid ,因爲要找到的是最短的連續區間;不滿足則 lo = mid + 1

代碼1:

class Solution {
public:
    int minSubArrayLen(int s, vector<int>& nums) {
        if (nums.empty()) return 0; 
        int lo = 0, hi = nums.size(); 
        while (lo < hi) { 
            int mid = lo + (hi - lo) / 2, maxWin = 0, nowWin = 0;
            for (int i = 0; i < nums.size(); ++i) {
                if (i < mid) maxWin += nums[i];
                else {
                    //求出固定爲mid長度的區間的最大區間和
                    nowWin = (i == mid) ? maxWin : nowWin;
                    nowWin = nowWin + nums[i] - nums[i - mid];
                    maxWin = max(nowWin, maxWin);
                }
            } //求出第一個滿足>=s的連續區間長度
            if (maxWin >= s) hi = mid;
            else lo = mid + 1;
        }
        if (lo >= nums.size()) {
            int sum = 0;
            for (int i = 0; i < nums.size(); ++i) 
                sum += nums[i];
            if (sum < s) lo = 0;
        }
        return lo;
    }
};

效率不高,但是能過:

執行用時:24 ms, 在所有 C++ 提交中擊敗了28.25% 的用戶
內存消耗:10.3 MB, 在所有 C++ 提交中擊敗了12.50% 的用戶

思路2:雖然是 O(nlogn)\text{O(nlogn)} ,但是反覆統計區間和浪費了太多的效率,用前綴和優化一下。

代碼2如下:

class Solution {
public:
    int minSubArrayLen(int s, vector<int>& nums) {
        if (nums.empty()) return 0; 
        int lo = 0, hi = nums.size(); 
        vector<int> preSum(nums.size() + 1, 0);
        for (int i = 0; i < nums.size(); ++i) preSum[i + 1] = preSum[i] + nums[i];
        while (lo < hi) { 
            int mid = lo + (hi - lo) / 2, maxWin = 0;
            for (int i = mid; i < preSum.size(); ++i) 
                maxWin = max(preSum[i] - preSum[i - mid], maxWin);
            //求出第一個滿足>=s的連續區間長度
            if (maxWin >= s) hi = mid;
            else lo = mid + 1;
        } 
        if (preSum.back() < s) lo = 0; //整個區間和都無法滿足條件
        return lo;
    }
};

效率:

執行用時:20 ms, 在所有 C++ 提交中擊敗了45.59% 的用戶
內存消耗:10.4 MB, 在所有 C++ 提交中擊敗了12.50% 的用戶

思路3:前面的滑動窗口都是固定長度的;如果我們允許窗口擴張和收縮,就能得到 O(n)\text{O(n)} 的解法。

代碼3:

class Solution {
public:
    int minSubArrayLen(int s, vector<int>& nums) {
        if (nums.empty()) return 0; 
        int winSum = 0, left = 0, right = 0, minLen = INT_MAX;
        while (right < nums.size()) {
            winSum += nums[right++]; //right不斷右移
            while (winSum >= s) { //滿足條件
                minLen = min(minLen, right - left); //修改長度
                winSum -= nums[left++]; //區間收縮
            }
        } //整個區間和都無法滿足條件
        return (minLen == INT_MAX) ? 0 : minLen;
    }
};

效率:

執行用時:16 ms, 在所有 C++ 提交中擊敗了77.76% 的用戶
內存消耗:10.3 MB, 在所有 C++ 提交中擊敗了12.50% 的用戶

爲什麼內存消耗還是這樣大?不明白。而且似乎沒有優化的地方了,但是還只是擊敗了 77% 的用戶???

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章