239. 滑动窗口最大值
力扣题目链接(opens new window)
给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回滑动窗口中的最大值。
进阶:
你能在线性时间复杂度内解决此题吗?
思路
这道题我一开始的想法,就是用dequeue队列进行操作,用长度为k的队列遍历所有字符,然后每一次访问都进行一次排序,由此下来复杂度是O(n k),于是有了以下结果
代码大概如下(错误的,不要学)
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
vector<int>res;
deque<int> q;
for(int n:nums){
if (q.size()<k){
q.push_back(n);
}else
{
int max = *max_element(q.begin(),q.end());
res.push_back(max);
q.pop_front();
q.push_back(n);
}
}
int max = *max_element(q.begin(),q.end());
res.push_back(max);
return res;
}
};
肯定有更简单的做法,这这道题还是要用队列来做的,但是却不是普通的队列。
我们如果想要复杂度只有O(n),那便不能做排序的操作,而是将最大值放在队列头部,随取随用。要做到这个操作首先有两个步骤
- 每次push时,将队列中小于当前值的元素pop掉
- 每次pop时,如果pop的值与队列头部相等,则执行pop操作
这里的push为自定义的push,如果每次push都将比当前值下的元素值pop掉,那么队列的头部必定是最大值,这是毋庸置疑的。
而有了第一步的操作时,我们在pop时就需要加一个判断,如果与队列头部(也即队列最大值)相等,可以说明一件事情,就是在pop之前,队列没有进行任何pop的操作,压进来的第一个元素便是最大值,而且后续元素是递减排列,因此不用取pop。只有在这种情况下才需要pop,其他情况早已经在push时顺手pop掉了。
有了思路了就好操作了,代码如下:
class Solution {
private:
class Myqueue{
private:
deque<int> myqueue;
public:
Myqueue(){}
void push(int val){
while(!myqueue.empty() && val > myqueue.back()){
myqueue.pop_back();
}
myqueue.push_back(val);
}
void pop(int val){
if (!myqueue.empty() && val == myqueue.front())
{
myqueue.pop_front();
}
}
int get_maxVale(){
return myqueue.front();
}
};
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
vector<int> res;
Myqueue myqueue;
for (int i = 0; i < k; ++i) {
myqueue.push(nums[i]);
}
res.push_back(myqueue.get_maxVale());
for (int i = k; i < nums.size(); ++i) {
myqueue.pop(nums[i-k]);
myqueue.push(nums[i]);
res.push_back(myqueue.get_maxVale());
}
return res;
}
};
res.push_back(myqueue.get_maxVale());
}
return res;
}
};