题目描述
给定一个数组 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
提示:
你可以假设 k 总是有效的,在输入数组不为空的情况下,1 ≤ k ≤ 输入数组的大小。
来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/hua-dong-chuang-kou-de-zui-da-zhi-lcof 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
分析:
这一题暴力模拟肯定都能写出,但肯定比的是优化
思路一:用一个大顶堆来维护这个滑动窗口?
每次需要从堆中去除第一个数,再将新的数加入这个堆
时间复杂度就是N入堆+N出堆
思路二:
仔细观察滑动的这个过程,其实有点像深度学习中的一个maxpooling的过程,保留一个区域内的max值
所以,其实我们可以记录,某一个数比较大,出现在滑动窗口中,那么之前比他小的数字都其实可以不用去操作了,直接删除,不用去做比较
单调队列的思想
我用一个单调队列来保存滑动窗口中出现的最大值 次大值等信息
这个信息因为和坐标有关,所以保存的时候需要记录坐标,注意,一个数字的左边小于它的数字,肯定是不用管的(需要删除)
可以简化一下,就用一个单独的队列来保存位置
至于具体的数值可以用索引来访问?
所以这一题我一开始想着用queue来做发现中间删除的操作很难完成
后来发现其实双端队列来实现确实不错
代码:
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
int n = nums.size();
deque<int> q;
vector<int> ans;
if(n==0) return ans;
for (int i = 0; i < k; ++i) {
while (!q.empty() && nums[i] >= nums[q.back()]) {
q.pop_back();
}
q.push_back(i);
}
ans = {nums[q.front()]};
for (int i = k; i < n; ++i) {
while (!q.empty() && nums[i] >= nums[q.back()]) {
q.pop_back();
}
q.push_back(i);
while (q.front() <= i - k) {
q.pop_front();
}
ans.push_back(nums[q.front()]);
}
return ans;
}
复杂度分析:
滑动窗口的过程是O(n)级别的
然后每次滑动的过程中的操作数量的常数级别的
所以是O(n)
然后空间复杂度的话需要一个双端队列,O(k),一个ans数组O(n)