【死磕演算法·棧和佇列】滑動視窗問題-雙端佇列
題目要求:
有一個整型陣列 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
題目思路:
遍歷陣列,用雙端佇列的隊首記錄滑動視窗內最大值的位置。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中加入當前滑動視窗最大值時,比較及加入的物件是具體的值,這裡細心處理即可。