這道題是從優先隊列的難題裏面找到的一個題目。但是解法並非優先隊列,而是雙項隊列deque
前端
其實只要知道思路,這一道題直接寫沒有太大的問題。咱們看看題數組
給定一個數組 nums,有一個大小爲 k 的滑動窗口從數組的最左側移動到數組的最右側。你只能夠看到在滑動窗口 k 內的數字。滑動窗口每次只向右移動一位。翻譯
返回滑動窗口最大值。code
示例:隊列
輸入: 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個最大值it
固然暴力naive的解法就顯而易見了,用O(n*k)能夠解決,不細說。但仍是手癢寫個僞代碼:io
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); }
我想到的另外一種方法是創建一個最大堆。class
想法很簡單:每次讀入一個新的數字,就把不在區間範圍內的數字移除堆裏,而後堆的頂部就是這次循環的結果。效率
一個堆其實很是容易實現。咱們甚至可使用一個C++ STL
中的priority_queue
。難點就在於,如何把這個不在區間範圍內的數字移除。容器
在這裏我介紹一種更快更通用的方法,使用雙項隊列。這裏爲了效率和快捷使用了deque
容器,也可使用list
容器。
我聲明瞭一個變量deque<int>window
,用於存儲下標。這個變量有如下特色:
window.front()
)是這次遍歷的最大值的下標window.back()
)比較,若是末尾比新數小,則把末尾扔掉,直到該隊列的末尾比新數大或者隊列爲空的時候才中止,作法有點像使用棧進行括號匹配。特色一隻是方便咱們獲取每次窗口滑動一格以後的最大值,咱們能夠直接經過window.front()
得到
經過特色二,能夠保證隊列裏的元素是從頭至尾降序的,因爲隊列裏只有窗口內的數,因此他們其實就是窗口內第一大,第二大,第三大...的數。
特色三就是根據題意設置的。但咱們實際上只用比較如今的下標和window.front()
就能夠了,想一想爲何?
Answer: 由於只要窗口內第一大元素也就是這個window.front()
在窗口內,那咱們能夠不用管第二大第三大元素在不在區間內了。由於答案必定是這個第一大元素。若是window.front()
不在窗口內,則將其彈出,第二個大元素變成第一大元素,第三大元素變成第二大元素以此類推。
代碼編寫的過程還要時刻檢查隊列是否爲空防止拋出異常。
根據上面這些信息咱們就能夠編寫此題的代碼了。
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; } };