給定一個數組 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;
}
};