劍指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]}。

解法一:暴力法,時間複雜度O(nk)

  1. 掃描每個滑動窗口的所有數字並找出其中的最大值。
  2. 如果滑動窗口的大小爲K,則需要O(k)時間找到最大值。
  3. 對於長度爲 n 的輸入數組,算法的時間複雜度爲O(nk)。
class Solution {
public:
    vector<int> maxInWindows(const vector<int>& num, unsigned int size)
    {
        vector<int>res;
        if(num.size()<size)
            return res;
        for(int i = 0, j=size-1; i<num.size()-size+1 && j<num.size(); i++, j++)
        {
            int M = num[i];
            for(int k = i; k<=j;k++) //遍歷窗口的內所有的值,找出最大值。
            {
                M = max(M, num[k]);
            }
            res.push_back(M);
        }
        return res;
            
    }
};

解法二:最大堆,時間複雜度O(nlogk)

由於要考慮最大堆堆頂數字是否超過了滑動窗口的範圍,所以最大堆中存放pair類型,第一個爲數組的數字,第二個是數組中數字對應的下標值。

  1. 採用最大堆結構
  2. 每次遍歷時,向堆中插入對應的pair,並檢查堆頂pair類型的數字是否超過華東窗口的範圍。沒有超出,則取堆頂pair類型的第一個位置的數據。
class Solution {
public:
    vector<int> maxInWindows(const vector<int>& num, unsigned int size)
    {
        vector<int>res;
        if(num.size()<size || size <= 0)
            return res;
        priority_queue<pair<int, int> >q;
        for(int i=0; i<size; i++)
        {
            q.push(make_pair(num[i], i));
        }
        res.push_back(q.top().first);
        for(int i=size; i<num.size(); i++)
        {
            q.push(make_pair(num[i], i));
            pair<int, int> p= q.top();
            while((i - p.second)>=size)
            {
                q.pop();
                p = q.top();
            }
            res.push_back(p.first);
        }
        return res;
    }
};

解法三:雙端隊列(deque),時間複雜度O(n)

算法思想:藉助一個輔助雙端隊列,步驟如下:

  1. 如果隊列爲空,則當前數字入隊列。
  2. 如果隊列不爲空且當前數字大於等於隊尾數字,則隊尾數字出隊直到隊尾元素小於當前待入隊的數字,或者在隊尾數字出隊的過程中,隊列爲空,則當前數字入隊。
  3. 如果當前數字小於隊尾數字,則當前數字入隊尾。
  4. 如果隊列頭部數字超出滑動窗口的範圍(比如,隊列頭部數字在數組中的位置是第一個,但是滑動窗口的範圍在第二個到第四個,那麼隊列頭部數字直接就可以刪除),則刪除隊列頭部數字(pop_front())。

由於要考慮隊列頭部數字是否超過了滑動窗口的範圍,所以隊列中存放的元素不能是原來的數組的數字,而應該是數組中數字的下標值。

class Solution {
public:
    vector<int> maxInWindows(const vector<int>& num, unsigned int size)
    {
        vector<int>res;
        if(num.size() < size || size <= 0)
            return res;
        deque<int>index;
        for(unsigned int i=0; i<size; i++)
        {
            while(!index.empty() && num[i] >= num[index.back()])
                index.pop_back();
            index.push_back(i);
        }
        for(unsigned int i=size; i<num.size(); i++)
        {
            res.push_back(num[index.front()]);
            while(!index.empty() && num[i] >= num[index.back()])
                index.pop_back();
            if(!index.empty() && index.front()<=(int)(i - size) )//如果隊列頭部數字超出滑動窗口的範圍,則刪除隊列頭部數字
                index.pop_front();
            index.push_back(i);
        }
        res.push_back(num[index.front()]);//滑到最後一個數字時,循環內部並沒有執行讀隊頭操作,所以這裏要留意下。
        return res;

    }
};

或者可以做如下修改。只是調整了其中兩行代碼的順序 

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

 

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