今天遇到一道滑动窗口最大值问题239滑动窗口最大值,发现单调队列是解决这类问题的高效方法,遂加以记录。
单调队列:即保持单调有序的队列。底层数据结构使用双端队列比较好,C++需要添加<deque>
头文件。
滑动窗口最大值问题中,我们关心的是滑动窗口内的最大值,当滑动窗口向右移动一位时,需要删除一个元素和添加一个元素。对于添加元素来说比较好办,如果添加的元素大于当前最大值则替换其为最大值。而删除元素,如果删除的是最大值,则需要重新遍历滑动窗口内容,造成效率的低下。
研究此问题发现,如下图,当添加的元素是4,时,前面的所有小于4的元素均变成无效数据(如果删除5,则4是最大值,轮不到123当最大值;如果删除了5又删除了4,因为123在4之前进入滑动窗口,所以删除4之前123已经被删除,所以也轮不到123当最大值)。
因此滑动窗口右移,一:删除窗口左端元素,如果和队首元素相同,则队首元素出队,否则什么也不做;二:添加窗口右边元素,新元素入队时,都将前面所有比自己小的元素删除再入队
//单调队列
struct MonotonicQue{
deque<int>que;
void push(int n){
while(!que.empty() && que.back()<n){
que.pop_back();
}
que.push_back(n);
}
void pop(int n){
if(n==que.front())
que.pop_front();
}
int top(){
return que.front();
}
};
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
MonotonicQue monQue;
//添加前k个元素到单调队列
for(int i=0;i<k;i++)
monQue.push(nums[i]);
vector<int>ans;
ans.push_back(monQue.top());
for(int i=k;i<nums.size();i++){
monQue.pop(nums[i-k]);
monQue.push(nums[i]);
ans.push_back(monQue.top());
}
return ans;
}
};