leetcode239. 滑动窗口最大值 (优先队列 单调队列)
链接:https://leetcode-cn.com/problems/sliding-window-maximum/
题目
给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回滑动窗口中的最大值。
用例
示例 1:
输入: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
示例 2:
输入:nums = [1], k = 1
输出:[1]
示例 3:
输入:nums = [1,-1], k = 1
输出:[1,-1]
示例 4:
输入:nums = [9,11], k = 2
输出:[11]
示例 5:
输入:nums = [4,-2], k = 2
输出:[4]
提示:
1 <= nums.length <= 105
-104 <= nums[i] <= 104
1 <= k <= nums.length
思路
方法1
优先队列
使用优先队列存储每次滑动窗口中的元素,可以直接获得最大的元素。
但是存在一个问题,是如何删除窗口向右滑动后窗口左侧的元素。所以可以存储元素的位置,在每次滑动后先判断最大值是否是在窗口范围内。
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
priority_queue<pair<int,int>>pq;
int n=nums.size();
for(int i=0;i<k;++i)
pq.push({nums[i],i});
vector<int>ans(n-k+1);
ans[0]=pq.top().first;
for(int i=k;i<n;++i){
pq.push({nums[i],i});
while(pq.top().second<i-k+1)
pq.pop();
ans[i-k+1]=pq.top().first;
}
return ans;
}
};
方法2
单调队列
使用优先队列获取最大值的复杂度为logn,加上滑动窗口的时间复杂度为O(nlogn)
优先队列主要解决的是快速选取窗口中的最大值,如何能做到O(n)情况下筛选最大值?
实际上对于一个窗口中最大值右侧的元素在后面选择中一定不会被选取,实际上只要保证按照顺序生成由大到小的序列,丢去小于当前值的序列尾部,序列的头部即为最大值
因为需要对序列头部和尾部进行操作,所以选择deque记录元素,针对deque头部元素检测序号是否在窗口内。
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
int n=nums.size();
deque<int>dq;
for(int i=0;i<k;++i){
while(!dq.empty()&&nums[dq.back()]<nums[i])
dq.pop_back();
dq.push_back(i);
}
vector<int>ans(n-k+1);
ans[0]=nums[dq.front()];
for(int i=k;i<n;++i){
while(!dq.empty()&&nums[dq.back()]<nums[i])
dq.pop_back();
dq.push_back(i);
while(dq.front()<i-k+1)
dq.pop_front();
ans[i-k+1]=nums[dq.front()];
}
return ans;
}
};

浙公网安备 33010602011771号