滑動視窗最大值——棧與佇列

小橘0815發表於2024-11-20

第一版程式碼:

class Solution {
private:
    class MyQueue { //單調佇列(從大到小)
    public:
        deque<int> que; // 使用deque來實現單調佇列
        // 每次彈出的時候,比較當前要彈出的數值是否等於佇列出口元素的數值,如果相等則彈出。
        // 同時pop之前判斷佇列當前是否為空。
        void pop(int value) {
            if (!que.empty() && value == que.front()) {
                que.pop_front();
            }
        }
        // 如果push的數值大於入口元素的數值,那麼就將佇列後端的數值彈出,直到push的數值小於等於佇列入口元素的數值為止。
        // 這樣就保持了佇列裡的數值是單調從大到小的了。
        void push(int value) {
            while (!que.empty() && value > que.back()) {
                que.pop_back();
            }
            que.push_back(value);

        }
        // 查詢當前佇列裡的最大值 直接返回佇列前端也就是front就可以了。
        int front() {
            return que.front();
        }
    };
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        MyQueue que;
        vector<int> result;
        for (int i = 0; i < k; i++) { // 先將前k的元素放進佇列
            que.push(nums[i]);
        }
        result.push_back(que.front()); // result 記錄前k的元素的最大值
        for (int i = k; i < nums.size(); i++) {
            que.pop(nums[i - k]); // 滑動視窗移除最前面元素
            que.push(nums[i]); // 滑動視窗前加入最後面的元素
            result.push_back(que.front()); // 記錄對應的最大值
        }
        return result;
    }
};

第二版程式碼如下:

第一個問題:如何實現滑動視窗, 透過單調遞減的單向佇列

第二個問題:這個構建過程,注意規則,裡面只有三個函式 彈出的話如果是遞減,滑動視窗移除的值等於視窗的front的值的話,那麼說明可以彈出,否則不用彈出

如何壓入呢,首先的得排完,需要把要進入視窗的值和佇列中的值進行比較,也就是和佇列中的back比較,假如back小那麼就把back刪瞭然後到最後那麼就形成了遞減的,然後再把它壓入即可

最後的函式就是front函式 返回佇列首值從而確保每次返回的都是最大值。不要把佇列和滑動視窗當成一個東西,佇列是為了維護視窗裡的最大值,每當視窗移動的時候我們要確保佇列的首部是最大值。

class Solution {
private:
    class MyQueue { //單調佇列(從大到小)
    public:
        deque<int> que; // 使用deque來實現單調佇列
        // 每次彈出的時候,比較當前要彈出的數值是否等於佇列出口元素的數值,如果相等則彈出。
        // 同時pop之前判斷佇列當前是否為空。
        void pop(int value) {
            if (!que.empty() && value == que.front()) {
                que.pop_front();
            }
        }
        // 如果push的數值大於入口元素的數值,那麼就將佇列後端的數值彈出,直到push的數值小於等於佇列入口元素的數值為止。
        // 這樣就保持了佇列裡的數值是單調從大到小的了。
        void push(int value) {
            while (!que.empty() && value > que.back()) {
                que.pop_back();
            }
            que.push_back(value);

        }
        // 查詢當前佇列裡的最大值 直接返回佇列前端也就是front就可以了。
        int front() {
            return que.front();
        }
    };
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        MyQueue que;
        vector<int> result;
        for (int i = 0; i < k; i++) { // 先將前k的元素放進佇列
            que.push(nums[i]);
        }
        result.push_back(que.front()); // result 記錄前k的元素的最大值
        for (int i = k; i < nums.size(); i++) {
            que.pop(nums[i - k]); // 滑動視窗移除最前面元素
            que.push(nums[i]); // 滑動視窗前加入最後面的元素
            result.push_back(que.front()); // 記錄對應的最大值
        }
        return result;
    }
};

重點在這個滑動視窗移動的for迴圈裡,先移除在加入, 然後注意這個單向佇列的設計邏輯,刪除的時候由於我們只關注出口的元素,所以只有當出口的元素等於視窗移除的元素時才執行移除

否則不相等的話是需要進行保留的,因為這個佇列裡面維護的是視窗裡的最大值元素,而當我們進行新增的時候也是要維持這個單調遞減的順序,否則前面的元素都進行刪除即可,同時注意到

這個移除的區別,前面的移除是對佇列的出口元素進行移除,後面的移除是對佇列的入口元素進行移除。然後就是最後的迴圈邏輯,前面已經有了k個元素所以這裡肯定要從k開始。

相關文章