1. 程式人生 > >【死磕演算法·棧和佇列】滑動視窗問題-雙端佇列

【死磕演算法·棧和佇列】滑動視窗問題-雙端佇列

題目要求:

有一個整型陣列 arr 和一個大小為 w 的視窗,從陣列的最左邊滑到最右邊,視窗每次向右邊滑一個位置。 返回一個長度為n-w+1的陣列res,res[i]表示每一種視窗狀態下的最大值。

以陣列為[4,3,5,4,3,3,6,7],w=3為例。因為第一個視窗[4,3,5]的最大值為5,第二個視窗[3,5,4]的最大值為5,第三個視窗[5,4,3]的最大值為5。第四個視窗[4,3,3]的最大值為4。第五個視窗[3,3,6]的最大值為6。第六個視窗[3,6,7]的最大值為7。所以最終返回[5,5,5,4,6,7]。

給定整形陣列arr及它的大小n,同時給定w,請返回res陣列。保證w

小於等於n,同時保證陣列大小小於等於500。

題目思路:

遍歷陣列,用雙端佇列的隊首記錄滑動視窗內最大值的位置。vector<int> result 記錄該滑動視窗內元素的最大值。

對於陣列元素arr[i]:

若佇列為空,將i加入佇列尾部;

若佇列不為空,將arr[i]和佇列隊尾元素x比較,若arr[i]大於x,將佇列隊尾元素彈出,直到arr[i]小於等於隊尾元素或佇列為空,將i加入佇列尾部;

對於一個滑動視窗,如果佇列的隊頭元素位置"過期",也就是小於這個滑動窗最左端的位置(i-w+1),將佇列的隊首元素彈出,新佇列的隊首元素作為滑動視窗的最大值加入result中。

為什麼比隊尾小於或等於的元素要壓入雙端佇列呢?

因為雙端佇列的隊首記錄著該滑動視窗最大元素的位置,要給它成為之後滑動視窗最大值的機會。

比隊尾大的元素要讓佇列尾部元素(代表比當前值小的陣列元素位置)不斷彈出,直到該元素<=索引等於佇列尾部值的陣列元素。因為當前元素值比隊尾元素索引位置的值要大,當前元素一定要在佇列的前面,才能隨著滑動視窗不斷移動,隊首元素不斷過期彈出,到達隊首,成為滑動視窗的最大值。

也就是要保證佇列從隊首到隊尾,元素對應的陣列值是遞減的。

圖解:

程式碼實現:

class SlideWindow {
public:
    vector<int> slide(vector<int> arr, int n, int w) {
        // write code here
        deque<int> s;
        vector<int> result;
        for(int i = 0;i<w;i++){
            if(s.empty())
                s.push_back(i);
            else{
                while(!s.empty()&&arr[i]>arr[s.back()])
                    s.pop_back();
                s.push_back(i);
            }
        }
        result.push_back(arr[s.front()]);
        for(int k = w;k<n;k++){
            if(s.empty())
                s.push_back(k);
            else{
                while(!s.empty()&&arr[k]>arr[s.back()])
                    s.pop_back();
                s.push_back(k);
            }
            if(s.front()<=k-w)
                s.pop_front();
            result.push_back(arr[s.front()]);
        }
        return result;
    }
};

注意點:

雙端佇列儲存的是陣列索引,在比較大小以及往result中加入當前滑動視窗最大值時,比較及加入的物件是具體的值,這裡細心處理即可。