LeetCode:239 滑動窗口最大值 雙端隊列 O(n)

給定一個數組 nums,有一個大小爲 k 的滑動窗口從數組的最左側移動到數組的最右側。你只可以看到在滑動窗口內的 k 個數字。滑動窗口每次只向右移動一位。

返回滑動窗口中的最大值。

示例:
輸入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
輸出: [3,3,5,5,6,7]
解釋:
在這裏插入圖片描述

提示:
你可以假設 k 總是有效的,在輸入數組不爲空的情況下,1 ≤ k ≤ 輸入數組的大小。

進階:
你能在線性時間複雜度內解決此題嗎?

來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/sliding-window-maximum
著作權歸領釦網絡所有。商業轉載請聯繫官方授權,非商業轉載請註明出處。

思路

使用雙端隊列存儲一個滑動窗口內的元素下標,這個雙端隊列必須滿足以下幾個特徵:

  • 隊頭元素是滑動窗口內最大元素的下標
  • 從隊頭到隊尾的下標對應的元素必須遞減
  • 存儲的下標必須在滑動窗口內

維護性質2
不斷枚舉滑動窗口的右端點r,如果nums[r]比隊尾元素對應的數nums[back()]大,那麼不斷彈出隊尾元素,直到隊尾大於nums[r](因爲比新數nums[r]小的數,都不能作爲區間的最大元素了,可以捨棄),新元素入隊尾

維護性質3
因爲右端點推進了,左端點l也要推進,還要檢查當前區間的最大值(隊頭存儲的下標)是否已經超過了當前的窗口,如果是,那麼隊頭要彈出

保存下標是爲了快速判斷存儲的元素是否超過滑動窗口的範圍,而元素遞減保證了最大元素超出區間時被彈出,隊頭仍然是最大元素

代碼

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k)
    {
        vector<int> ans;
        if(k==0) return ans;
        if(k==1) return nums;
        deque<int> q;
        // 初始化隊列
        for(int i=0; i<k; i++) 
        {
            while(!q.empty() && nums[i]>nums[q.back()]) 
                q.pop_back();
            q.push_back(i);
        }
        ans.push_back(nums[q.front()]);
        // 開始滑動,彈出超出區間或小的元素,進來新元素
        // l+k-1:區間右端點下標
        for(int l=1; l+k-1<nums.size(); l++)
        {
            if(l-1==q.front()) q.pop_front();
            while(!q.empty() && nums[l+k-1]>nums[q.back()]) 
                q.pop_back();
            q.push_back(l+k-1);
            ans.push_back(nums[q.front()]);
        }
        return ans;
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章