一、介绍
1.题目描述
题目链接:https://leetcode-cn.com/problems/sliding-window-maximum/
给你一个整数数组 nums
,有一个大小为 k
的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k
个数字。滑动窗口每次只向右移动一位。
- 返回滑动窗口中的最大值。
注意事项:
- 如果 k = 1,可以直接返回
2.测试样例
[1,3,-1,-3,5,3,6,7]
3
# [3,3,5,5,6,7]
[1]
1
# [1]
[1,-1]
1
# [1,-1]
[9,11]
2
# [11]
[4,-2]
2
# [4]
[7,2,4]
2
# [7,4]
二、题解
1、优先队列🔴
- 创建一个优先队列(大顶堆),记录数值和对应下标。
- 先对第一个窗口中的内容进行处理,加入队列中,往 ans 中加入第一个最大元素。
- 对于接下来的窗口内容,依次插入,判断当前最大值是否在窗口中
- 如果在窗口中,加入到答案
- 如果不在,删除该堆顶
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
if(k==1) return nums;
int n=nums.size();
// q 为优先队列,ans 记录答案数组
priority_queue<pair<int,int>> q;
// 处理第一个窗口
for(int i=0;i<k;i++) q.emplace(nums[i],i);
// 加入第一个元素
vector<int> ans={q.top().first};
// 遍历之后的窗口
for(int i=k;i<n;i++){
q.emplace(nums[i],i);
// 如果在滑动窗口外,弹出
while(q.top().second<=(i-k)) q.pop();
// 记录当前窗口中的最大值
ans.push_back(q.top().first);
}
return ans;
}
};
2、双端单调队列🟢
- 创建一个双端队列,遍历插入当前数的下标,为保证递增:
- 若当前数比较大,弹出队列中所有小于当前数的下标
- 否则直接插入
- 第一个窗口遍历完,队列头就是第一个答案
- 遍历之后窗口时
- 若队列头下标不在窗口内,则弹出队头
- 若当前数比较大,弹出队列中所有小于当前数的下标
- 否则直接插入
- 最后剩下的队列头就是当前窗口的最大值下标
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
if(k==1) return nums;
int n=nums.size();
// 双端队列
deque<int> q;
// 处理第一个窗口
for(int i=0;i<k;i++){
// 如果队列内的数小于当前数,弹出
while(!q.empty()&&nums[i]>=nums[q.back()]){
q.pop_back();
}
// 插入当前数
q.push_back(i);
}
// ans 记录答案
vector<int> ans={nums[q.front()]};
// 处理接下来的窗口
for(int i=k;i<n;i++){
// 下标超出窗口,弹出
while(q.front()<=i-k) q.pop_front();
// 如果队列内的数小于当前数,弹出
while(!q.empty()&&nums[i]>=nums[q.back()]){
q.pop_back();
}
q.push_back(i);
// 当前队列头为当前窗口内最大值下标
ans.push_back(nums[q.front()]);
}
return ans;
}
};