1. 程式人生 > >[LeetCode 239] Sliding Window Maximum (Queap/佇列快速求最大值)

[LeetCode 239] Sliding Window Maximum (Queap/佇列快速求最大值)

239. Sliding Window Maximum

Given an array nums, there is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the window. Each time the sliding window moves right by one position.

For example,
Given nums = [1,3,-1,-3,5,3,6,7], and k = 3.

Window position Max
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
Therefore, return the max sliding window as [3,3,5,5,6,7].

題解:

  題目需要快速求連續區間的最大值,為了使總體複雜度為線性,需要求最大值的複雜度為O(1),因此不能使用線段樹、樹狀陣列等方法來求。得益於我們需要求的區間每次移動只有1個單位,我們可以通過佇列來模擬這個區間的運動,而佇列通過巧妙的設計可以做到O(1)求佇列最值。(Queap)
  為了給佇列增加getMax()操作,我們需要多設定一個雙端佇列deq維護最大值的資訊。在push一個新值x時,deq也在隊尾新增1個值x,如果前面的值比x小,則將其彈出,直到隊空或遇到比x大的元素,然後填充回等於彈出數量的x,最後再將x加入。因為填充的全是一樣的值x,因此可以只加入一個元素,然後給這個元素一個計數值cnt表示x的數量。(這是減少複雜度的關鍵)
  當進行pop操作時,deq也需要將隊首出隊,因為deq中的元素有計數值cnt,因此當cnt大於1時,將cnt減1,否則才彈出。在push和pop中這樣維護後,可以發現deq的隊首元素就是當前佇列的最大值,而且不難驗證,push和pop的複雜度均攤後仍然是O(1)的。
  得到這個佇列之後,求得需要的向量是簡單的。總體的時間複雜度為O(n)

程式碼:

#include <algorithm>
#include <queue>

struct Cnt {
    int val;
    int cnt;
    Cnt(int val, int cnt) : val(val), cnt(cnt) {}
};

class Queap {
public:
    Queap() {}
    void push(int x);
    int pop();
    int getMax();
private:
    queue<int> que;
    deque<Cnt> cnt;
};

void Queap::push(int x)
{
    que.push(x);

    int count = 1;
    while (!cnt.empty() &&  x > cnt.back().val) {
        count += cnt.back().cnt;
        cnt.pop_back();
    }
    cnt.push_back(Cnt(x, count));
}

int Queap::pop()
{
    int ret = que.front(); que.pop();
    if (cnt.front().cnt > 1) {
        cnt.front().cnt -= 1;
    } else cnt.pop_front();

    return ret;
}

int Queap::getMax()
{
    return cnt.front().val;
}

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        Queap que;
        vector<int> ret;
        if (nums.size() == 0) return vector<int>();

        int i = 0;
        for (; i < k; i++)
            que.push(nums[i]);
        ret.push_back(que.getMax());

        for (; i < nums.size(); i++) {
            que.pop();
            que.push(nums[i]);
            ret.push_back(que.getMax());
        }
        return ret;
    }
};