劍指offer - 滑動窗口最大值 (單調隊列的使用)& POJ 2823 & leetcode 239. 滑動窗口最大值

給定一個數組和滑動窗口的大小,找出所有滑動窗口裏數值的最大值。例如,如果輸入數組{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]}。

牛客網上劍指offer的題目(書上似乎沒這個題?)

題意很簡單,給你一個長度爲N的數組,對於(0,N-K+1)範圍的每個數,求出i-i+K-1範圍內的最大值。
最開始想到的是RMQ查詢或者線段樹,但感覺這種做法很傻逼,應該不是劍指offer的做法。後來看了下,用單調的雙向隊列即可。就是維護一個雙向的隊列,隊列裏面是有序的,對於當前i,如果從隊列尾部開始查到適合i插入的位置,並把後面的數全部刪除。
代碼如下:

class Solution {
public:
    vector<int> maxInWindows(const vector<int>& num, unsigned int size)
    {
        vector<int> res;
        if(size <= 0 || num.size() == 0)    return res;
        deque<int> d;
        for(int i=0;i<num.size();++i){
            while(d.size() > 0 && num[d.back()] <= num[i]){
                d.pop_back();
            }
            d.push_back(i);
            if(i +1 >= size){
                res.push_back(num[d.front()]);
                if(d.front() == i+1-size)
                    d.pop_front();
            }
        }
        return res;
    }
};

//這裏在G++下會超時,C++可以通過,應該是G++對STL的優化不太好。
POJ 2823 題目基本相似,不過是要求最大值和最小值,所以需要維護兩個雙端隊列
代碼如下:

#include<iostream>
#include<string>
#include<algorithm>
#include<cmath>
#include<deque>
#include<cstdio>
using namespace std;
const int MAX = 1e6+10;
int a[MAX],N,K;
deque<int> Max,Min;
int Maxvalue[MAX],Minvalue[MAX];
void solve(){
    for(int i=1;i<=N;++i){
        while(!Max.empty() && a[Max.back()] < a[i])
            Max.pop_back();
        Max.push_back(i);
 //       printf("MaxD = %d\n",Max.v[Max.s]);
        while(!Min.empty() && a[Min.back()] > a[i])
            Min.pop_back();
        Min.push_back(i);
        if(i - K +1 > 0){
            Maxvalue[i-K+1] = a[Max.front()];
            Minvalue[i-K+1] = a[Min.front()];
            if(Min.front() == i-K+1)
                Min.pop_front();
            if(Max.front() == i-K+1)
                Max.pop_front();
        }
    }
    for(int i=1;i<=N-K+1;++i){
        printf("%d%c",Minvalue[i],i==N-K+1?'\n':' ');
    }
    for(int i=1;i<=N-K+1;++i){
        printf("%d%c",Maxvalue[i],i==N-K+1?'\n':' ');
    }
}
//#define test
int main(void){
    #ifdef test
        freopen("in.txt","r",stdin);
    #endif // test
    while(scanf("%d%d",&N,&K) != EOF){
        for(int i=1;i<=N;++i){
            scanf("%d",&a[i]);
        }
        solve();
    }
    #ifdef test
        fclose(stdin);
    #endif // test

    return 0;
}

leetcode 239
做法同上,要注意維護的是下標,不是值,如果在雙端隊列插入值的話,無法判斷當前的最大值是否已經超過了這個區間。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章