59-I
题目描述
解法 双端队列
维护一个back到front不断增大的双端队列,队首为滑动窗口的最大值,当窗口移动一位时,有以下两个动作。
- 进入新的元素,从队尾开始,不断将小于他的元素pop,直到找到大于它的元素再将其压入队尾。
这不难理解,新进入的数字大于队内的数字,说明队内的数字是绝对没有意义的,不可能成为最大值,并且出去的时刻肯定比新数字早。 - 弹出旧的元素,只需要判断队首的元素和弹出的元素是否相同,相同则弹出。
可能就会问,那队首被弹出之后,新的元素又不是移动窗口的最大元素,怎么知道移动窗口的最大元素还在队首呢。我们假设最大元素此时在队首,后面又进入了比它更小的元素,如果这个元素有作为下一个窗口的最大值的潜质,一定会被保存在队首的下一位,这时候队首出队它就是最大元素了。
总结来说就是遇到新的数字时,之前的小数字就直接抛弃,新的数字先放进去再说。
class Solution {
public:
void popNum(deque<int>& que, int value) {
if(!que.empty() && que.front() == value) que.pop_front();
}
void pushNum(deque<int>& que,int value) {
while(!que.empty() && que.back() < value) que.pop_back();
que.push_back(value);
}
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
if(nums.size()==0) return {
};
deque <int> que;
vector<int> ans;
//先将处理前k个元素
for(int i = 0; i < k; i ++) {
pushNum(que, nums[i]);
}
//前k个元素的最大值
ans.push_back(que.front());
for(int i = k; i < nums.size(); i++) {
pushNum(que, nums[i]);
popNum(que, nums[i - k]);
ans.push_back(que.front());
}
return ans;
}
};
时间复杂度O(N),每个元素最多被pop push各一次
空间复杂度O(K)
59-II
题目描述
解法 辅助双端队列
沿袭刚才的思路,建立一个维护最大值的辅助双端队列即可,然后再建立一个一般的队列用于pop和push,pop的时候判断一下双端队列的队首是不是被pop了
class MaxQueue {
public:
//获取最大值用的
deque<int> que;
//存储数字
queue<int> q;
MaxQueue() {
}
int max_value() {
if(que.empty()) return -1;
return this -> que.front();
}
void push_back(int value) {
while(!this -> que.empty() && value > this -> que.back()) this -> que.pop_back();
this -> que.push_back(value);
this -> q.push(value);
}
int pop_front() {
if(q.empty()) return -1;
if(q.front() == que.front()) que.pop_front();
int ans = q.front();
q.pop();
return ans;
}
};
/**
* Your MaxQueue object will be instantiated and called as such:
* MaxQueue* obj = new MaxQueue();
* int param_1 = obj->max_value();
* obj->push_back(value);
* int param_3 = obj->pop_front();
*/
时间复杂度O(1)
空间复杂度O(N)