這道題是從優先佇列的難題裡面找到的一個題目。可是解法並不是優先佇列,而是雙項佇列deque
其實只要知道思路,這一道題直接寫沒有太大的問題。我們看看題
給定一個陣列 nums,有一個大小為 k 的滑動視窗從陣列的最左側移動到陣列的最右側。你只可以看到在滑動視窗 k 內的數字。滑動視窗每次只向右移動一位。
返回滑動視窗最大值。
示例:
輸入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
輸出: [3,3,5,5,6,7]
解釋:
滑動視窗的位置 最大值
--------------- -----
[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
翻譯一下就是: 滑動區間裡面每K個最大值
當然暴力naive的解法就顯而易見了,用O(n*k)可以解決,不細說。但還是手癢寫個虛擬碼:
for(size_t i=0;i<nums.size();i++){
int maxValue=INT_MIN;
for(size_t j=i;j<i+k;j++){
maxValue=max(maxValue,nums[j]);
}
res.push_back(maxValue);
}
我想到的另一種方法是建立一個最大堆。
想法很簡單:每次讀入一個新的數字,就把不在區間範圍內的數字移除堆裡,然後堆的頂部就是此次迴圈的結果。
一個堆其實非常容易實現。我們甚至可以使用一個C++ STL
中的priority_queue
。難點就在於,如何把這個不在區間範圍內的數字移除。
However
在這裡我介紹一種更快更通用的方法,使用雙項佇列。這裡為了效率和快捷使用了deque
容器,也可以使用list
容器。
我宣告瞭一個變數deque<int>window
,用於儲存下標。這個變數有以下特點:
- 變數的最前端(也就是
window.front()
)是此次遍歷的最大值的下標 - 當我們遇到新的數時,將新的數和雙項佇列的末尾(也就是
window.back()
)比較,如果末尾比新數小,則把末尾扔掉,直到該佇列的末尾比新數大或者佇列為空的時候才停止,做法有點像使用棧進行括號匹配。 - 雙項佇列中的所有值都要在視窗範圍內
特點一隻是方便我們獲取每次視窗滑動一格之後的最大值,我們可以直接通過window.front()
獲得
通過特點二,可以保證佇列裡的元素是從頭到尾降序的,由於佇列裡只有視窗內的數,所以他們其實就是視窗內第一大,第二大,第三大...的數。
特點三就是根據題意設定的。但我們實際上只用比較現在的下標和window.front()
就可以了,想想為什麼?
Answer: 因為只要視窗內第一大元素也就是這個window.front()
在視窗內,那我們可以不用管第二大第三大元素在不在區間內了。因為答案一定是這個第一大元素。如果window.front()
不在視窗內,則將其彈出,第二個大元素變成第一大元素,第三大元素變成第二大元素以此類推。
程式碼編寫的過程還要時刻檢查佇列是否為空防止丟擲異常。
根據上面這些資訊我們就可以編寫此題的程式碼了。
Solution
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
if(k==0)return {};
vector<int>res;
deque<size_t>window;
/*Init K integers in the list*/
for (size_t i = 0; i < k; i++) {
while (!window.empty() && nums[i] > nums[window.back()]) {
window.pop_back();
}
window.push_back(i);
}
res.push_back(nums[window.front()]);
/*End of initialization*/
for (size_t i = k; i < nums.size(); i++) {
if (!window.empty() && window.front() <= i - k) {
window.pop_front();
}
while (!window.empty() && nums[i] > nums[window.back()]) {
window.pop_back();
}
window.push_back(i);
res.push_back(nums[window.front()]);
}
return res;
}
};