給定一個數組 nums,有一個大小為 k 的滑動視窗從數組的最左側移動到數組的最右側。你隻可以看到在滑動視窗内的 k 個數字。滑動視窗每次隻向右移動一位。
傳回滑動視窗中的最大值。
示例:
輸入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
輸出: [3,3,5,5,6,7]
解釋:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICM38FdsYkRGZkRG9lcvx2bjxiNx8VZ6l2cs0TPn5UNZpmTzUEROBDOsJGcohVYsR2MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL0cTOxQjNwETMyAzMwAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
提示:
你可以假設 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;
}
};