leetcode 239.Sliding Window Maximum
You are given an array of integers nums, there is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the window. Each time the sliding window moves right by one position.
Return the max sliding window.
Example 1:
Input: nums = [1,3,-1,-3,5,3,6,7], k = 3
Output: [3,3,5,5,6,7]
Example 2:
Input: nums = [1], k = 1
Output: [1]
Example 3:
Input: nums = [1,-1], k = 1
Output: [1,-1]
Example 4:
Input: nums = [9,11], k = 2
Output: [11]
Example 5:
Input: nums = [4,-2], k = 2
Output: [4]
Constraints:
1 <= nums.length <= 105
-104 <= nums[i] <= 104
1 <= k <= nums.length
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/sliding-window-maximum
这题可以用双端单调队列来解决这道题:
因为在窗口向前滑动的过程中,我们会发现,每次窗口向前滑动的时候,要添加一个数的同时还要减少一个数,添加一个新的元素进去,如果这个新的元素比窗口内的一些数要大,那么这些比新元素要小的数,直到被淘汰都没有机会露个面。
eg: 序列 [10,7,5],6,4,3
在窗口向前滑动后,新数据6比5要大,那么,这个5直到被淘汰都不可能是最大的数,因为有个6一直打压 着它。
10,[7,5,6],4,3 ---------------------- Max = 7
10,7,[5,6,4],3 ---------------------- Max = 6
10,7,5,[6,4,3] ---------------------- Max = 6
那么我们可以在遍历一遍序列时,在压入一个新数据的时候,将之前比它小的所有数字,全都删除,只保留比它大的数字,那么最终得到的序列一定是个单调递减的序列,这就是单调队列。
那为什么要双端队列呢,因为在添加新数据进入队列,以及删除比新数据要小的数据都是在队尾实现,但要获得每次移动窗口后最大的数据的值,就得要队头元素,用queue.front()来获取,而且窗口向前滑动,得删除掉滑动窗口左端的元素,所以得用双端单调队列。
代码:
class Solution {
private:
deque<int> data; //double-ended queue
public:
void push(int DATA)
{
//在队尾添加元素,把前面比新元素小的元素都删除掉
while(!data.empty() && data.back() < DATA){
data.pop_back();
}
data.push_back(DATA);
} //形成单调递减序列
int max(){ return data.front(); }
void pop(int DATA){
/*滑动窗口向前移动一位,右边加一位,左边得减一位,但如果已经被压扁了,
也就是说队列里面已经不存在最左边的那个数,就不用删除,否则就得删除掉这个数*/
if(!data.empty() && data.front() == DATA){
data.pop_front();
}
};
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
Solution window;
vector<int> res;
for(int i = 0; i < nums.size(); i++){
if(i < k - 1){ //先填满窗口的前K - 1
window.push(nums[i]);
}
else{ //窗口向前滑动
window.push(nums[i]);
res.push_back(window.max());
window.pop(nums[i - k + 1]); //指滑动窗口最左边的数字,查看它是否在队列中,有则删除掉
}
}
return res;
}
};
洛谷P1886 滑动窗口 /【模板】单调队列
洛谷同样有这道题,只是多了个求最小值。思路是一样的。
输入 #1
8 3
1 3 -1 -3 5 3 6 7
输出 #1
-1 -3 -3 -3 3 3
3 3 5 5 6 7
链接:https://www.luogu.com.cn/problem/P1886
#include<iostream>
#include<deque>
#include<cstdio>
#include<vector>
using namespace std;
long long int res[1000001] = { 0 }, res1[1000001] = { 0 };
void push(deque<long long int>& line, long long int data, int flag)
{
if (flag == 1){
while (!line.empty() && line.back() < data)
line.pop_back();
line.push_back(data);
}
else{
while (!line.empty() && line.back() > data)
line.pop_back();
line.push_back(data);
}
}
void pop_data(deque<long long int>& line, long long int data)
{
if (!line.empty() && line.front() == data)
line.pop_front();
}
int main(){
deque<long long int> q, qq;
int i, n, k, len1 = 0, len2 = 0;
long long int xx;
vector<long long int> nums;
cin >> n >> k;
for (i = 0; i<n; i++){
cin >> xx;
nums.push_back(xx);
}
for (i = 0; i < n; i++){
if (i < k - 1){
push(q, nums[i], 1);
push(qq, nums[i], 2);
}
else{
push(q, nums[i], 1);
push(qq, nums[i], 2);
res[len1++] = q.front();
res1[len2++] = qq.front();
pop_data(q, nums[i - k + 1]);
pop_data(qq, nums[i - k + 1]);
}
}
for (i = 0; i<len2; i++)
printf("%lld ", res1[i]);
printf("\n");
for (i = 0; i<len1; i++)
printf("%lld ", res[i]);
return 0;
}