1. 程式人生 > 實用技巧 >[64]劍指offer->滑動視窗的最大值

[64]劍指offer->滑動視窗的最大值

主目錄

題目

給定一個數組和滑動視窗的大小,找出所有滑動窗口裡數值的最大值。

例如,如果輸入陣列{2,3,4,2,6,2,5,1}及滑動視窗的大小3,那麼一共存在6個滑動視窗,他們的最大值分別為{4,4,6,6,6,5};

針對陣列{2,3,4,2,6,2,5,1}的滑動視窗有以下6個:
{[2,3,4],2,6,2,5,1},
{2,[3,4,2],6,2,5,1},
{2,3,[4,2,6],2,5,1},
{2,3,4,[2,6,2],5,1},
{2,3,4,2,[6,2,5],1},
{2,3,4,2,6,[2,5,1]}。
視窗大於陣列長度的時候,返回空

思路

我們可以使用一個雙端佇列deque。

陣列的第一個數字是2,把它存入佇列中。
第二個數字是3,比2大,所以2不可能是滑動視窗中的最大值,因此把2從佇列裡刪除,
再把3存入佇列中。
第三個數字是4,比3大,同樣的刪3存4。
此時滑動視窗中已經有3個數字,而它的最大值4位於佇列的頭部。
第四個數字2比4小,但是當4滑出之後它還是有可能成為最大值的,所以我們把2存入佇列的尾部。
下一個數字是6,比4和2都大,刪4和2,存6。
就這樣依次進行,最大值永遠位於佇列的頭部。

但是我們怎樣判斷滑動視窗是否包括一個數字?
應該在佇列裡存入數字在數組裡的下標,而不是數值。
當佇列中首個成員的下標與當前處理的數字的下標之差大於或者相等於滑動視窗大小時,這個數字已經從視窗中滑出,可以從佇列中刪除。

整體過程示意圖

程式碼

class Solution {
public:
    vector<int> maxInWindows(const vector<int>& num, unsigned int size)
    {
        vector<int> res;
        deque<int> s;
        for(unsigned int i=0;i<num.size();++i){

            //從後面依次彈出佇列中比當前num值小的元素,同時也能保證佇列首元素為當前視窗最大值下標
            while(s.size() && num[s.back()]<=num[i])
                s.pop_back();

            //噹噹前視窗移出隊首元素所在的位置,即隊首元素座標對應的num不在視窗中,需要彈出
            while(s.size() && i-s.front()+1>size)
                s.pop_front();

            s.push_back(i);//把每次滑動的num下標加入佇列

            if(size&&i+1>=size)//當滑動視窗首地址i大於等於size時才開始寫入視窗最大值
                res.push_back(num[s.front()]);
        }
        return res;
    }
};