难度简单113
给定一个数组 nums
和滑动窗口的大小 k
,请找出所有滑动窗口里的最大值。
示例:
输入: nums =[1,3,-1,-3,5,3,6,7]
, 和 k = 3 输出:[3,3,5,5,6,7] 解释:
滑动窗口的位置 最大值 --------------- ----- [1 3 -1] -3 5 3 6 7 3 1 [3 -1 -3] 5 3 6 7 3 1 3 [-1 -3 5] 3 6 7 5 1 3 -1 [-3 5 3] 6 7 5 1 3 -1 -3 [5 3 6] 7 6 1 3 -1 -3 5 [3 6 7] 7
解题思路
学习了 @代码随想录 大佬的解法,我来梳理一下我的理解
**建议用一个数组模拟一遍流程更容易理解到这种单调队列的思想。**
我们定义一个单调队列(单调递增或单调递减的,这里用到的是单调递增),它的用处就是每次队首就是最大值,即我们滑动一次窗口返回队首即为最大值。
那么这个怎么实现呢?
它的结构应该是这样的:
``` cpp
class Myqueue{
public:
deque<int> que;
void push(int value) {
}
void pop(int value){
}
int front(){
return que.front();
}
};
```
- push规则:
就是我们每次放入元素value,都要进行这样的操作:如果que不为空并且value > que.back(),就是大于队尾元素,就将队尾元素弹出que.pop_back();直到不满足上述两个条件,才把当前元素放入队列,这其实就是在push每个元素的时候进行了比较,保证元素是递减的。
- pop规则:
由push规则我们知道,que队列中可能并没有完整的nums数组中的所有元素,那么当滑动窗口移动时,怎么模拟pop出K个元素中的第一个呢(value对应代码中的nums[j - k])?它是这样的:如果pop的值value == que.front(),就进行pop。其他值不进行操作,因为不影响,在que中也不一定还存在。主要是如果窗口中要移除的那个元素就算是最大值,也一样得移除,所以pop的时候要进行判断。这是比较难理解的一个点。
- front规则:
直接返回的就是最大值。
其他见代码,结合代码看起来更好理解一些。这种写法时间复杂度O(N),但是beats率却不如我之前写的暴力法,我看了一下18个测试用例,如果多一些可能优势就体现出来了。
其他:巩固了deque用法,如:
1. 定义:deque<int> que
2. API:队首front()、队尾back()、弹出队首pop_front()、弹出队尾pop_back()
3. push_back(),放进队尾
我又想起常用的queue的一些用法:
1. 定义:queue<int> que
2. API:队首front()、队尾top()
3. push,直接就放进队尾
代码
```cpp
class Solution {
public:
class Myqueue{
public:
deque<int> que;
void push(int value) {
while(!que.empty() && value > que.back()){
que.pop_back();
}
que.push_back(value);
}
void pop(int value){
if(!que.empty() && value == que.front()){
que.pop_front();
}
}
int front(){
return que.front();
}
};
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
Myqueue my_queue;
vector<int> result;
if(nums.size() == 0) return result;
for(int i = 0; i < k; i++){
my_queue.push(nums[i]);
}
result.push_back(my_queue.front());
//开始移动滑动窗口
for(int j = k; j < nums.size(); j++ )
{
my_queue.pop(nums[j - k]);
my_queue.push(nums[j]);
result.push_back(my_queue.front());
}
return result;
}
};
```