【程式碼隨想錄】一、陣列:4.滑動視窗

henFiu發表於2024-08-15

1.題目1:209. 長度最小的子陣列

1.1.解法1:暴力解法(已超時)

使用兩層迴圈,外層迴圈確定最小子陣列開始的位置(i),內層迴圈確定最小子陣列結束的位置(j)。在每次跳出內層迴圈時,sum應重置為0。
當找到的子陣列相加的和等於或大於目標值(target)時,算出此刻子陣列的長度(j - i + 1),並更新result的值。
最終在外層迴圈遍歷完所有元素時,對result值進行比較,如果result的值沒有發生變化,則不存在符合條件的子陣列,返回0。

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int n = nums.size(), res = INT_MAX;
        for (int i = 0; i < n; i++) {
            int sum = 0;
            for (int j = i; j < n; j++) {
                sum += nums[j];
                if (sum >= target) {
                    res = min(res, j - i + 1);
                    break;
                }
            }
        }
        return res == INT_MAX ? 0 : res;
    }
};

● 時間複雜度:\(O(n^2)\)
● 空間複雜度:\(O(1)\)

1.2.解法2:滑動視窗

讓i最開始時指向陣列起始位置(視窗起始位置),當子陣列和大於目標值時,確定視窗的終止位置(j)。將視窗進行縮小,即將i所指位置向前推,相應sum減去i之前元素的值。
舉個例子:
nums = [1,1,1,1,100],target = 100。
當j指向100時,開始縮小視窗。i增大,sum減去1、1、1、1。

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int res = INT_MAX; 
        int i = 0; // 滑動視窗起始位置
        int sum = 0; // 滑動視窗數值之和
        for (int j = 0; j < nums.size(); j++) {
            sum += nums[j];
            // 每次更新i,比較子序列是否符合要求
            while (sum >= target) {
                res = min(res, j - i + 1); // 取子序列長度
                sum -= nums[i++];
            }
        }
        // 若res沒有被賦值,返回0,沒有符合條件的子序列
        return res == INT_MAX ? 0 : res;
    }
};

● 時間複雜度:\(O(n)\)
● 空間複雜度:\(O(1)\)

2.題目2:904.水果成籃

2.1.解法1:滑動視窗

這題與209的思考過程非常相似,但比較有意思的是使用了unordered_map,它與209中的sum使用方法是類似的。
簡述我的思考過程:
據題意,只能保留兩個型別的水果,並且需要連續收集儘可能多的水果數量。(即為只出現兩個數字且長度最大的連續子陣列)。
為了統計當前遍歷到的水果型別的數目,使用unordered_map來進行統計:<水果型別,該型別水果數目>,記為unordered_map<int, int>t。
我們設定一個指標j,一直遍歷陣列,另一個指標i,指向陣列的起始位置。
當j遍歷時,將水果加入t中,t[fruit[j]]++,fruit[j]為水果型別,t[fruit[j]]為當前統計的該型別水果的數目。
一旦當t.size()大於2時,我們需要將i所指水果減去,更新i所指位置,直至該視窗中僅包含兩種型別的水果,j才繼續向後遍歷。
res始終記錄視窗最大長度:res = max(res, j - i + 1)。

在本題中實現滑動視窗,主要確定如下三點:
● 視窗內是什麼?
● 如何移動視窗的起始位置?
● 如何移動視窗的結束位置?
視窗就是:只出現兩個數字且長度最大的連續子陣列。
視窗的起始位置如何移動:如果水果型別(unordered_map中元素)大於2,視窗就要向前移動,使得型別仍為2。
視窗的結束位置如何移動:視窗的結束位置就是遍歷陣列的指標,也就是for迴圈裡的索引。

class Solution {
public:
    int totalFruit(vector<int>& fruits) {
        int res = 0;
        int i = 0;
        unordered_map<int, int>t;
        for (int j = 0; j < fruits.size(); j++) {
            t[fruits[j]]++;
            while (t.size() > 2) {
                auto it = t.find(fruits[i]);
                it->second--;
                if (it->second == 0) t.erase(it);
                i++;
            }
            res = max(res, j - i + 1);
        }
        return res;
    }
};

● 時間複雜度:O(n)。
● 空間複雜度:O(1)。

相關文章