week5 D-滑动窗口 单调队列
程序员文章站
2022-05-04 17:15:49
...
题意:
给定一个数组(大小为n),以及一个窗口(大小为k)。窗口在数组上从开始到结尾移动,输出每个位置的窗口里的最大值和最小值。
如图:
input:
两行数字,第一行两个数字,分别为数组的大小和窗口的大小,其中1<=k<=n<=1000000。
output:
输出有两行。第一行输出滑动窗口在从左到右的每个位置时,滑动窗口中的最小值。第二行是最大值。
思路:
一、可以暴力,依次找出每个串口中的最大值和最小值,但会超时。
二、单调队列,用单调递增队列找出每个窗口的最小值,用单调递减队列找到每个窗口的最大值。以找最小值为例,首先初始化队列,即先将第一个窗口内的值按要求加入队列中去。即当前要加入的数的值如果大于等于队列尾部的值,则直接入队列。否则队尾指针tail–,直到队列为空或者可以大于队尾的值的时候,将其入队列。初始化后,队列第一个数即第一个窗口的最小的值。其后定义窗口左端点和右端点l,r。在每一轮循环中,l++,r++,即窗口移动一个位置。在移动中r对应的数即为要入队列的数,其操作与初始化队列的操作相同。但要判断当前的队首的元素是否在当前的的窗口内,即top如果在当前的队列外,队首出队列,即top++。在窗口每移动一次,便可得到一个当前窗口的最小值。当r到达最右端的n时,循环结束。得到每一个窗口的最小值。最大值得实现与此相同。最后输出即可。
代码:
#include <cstdio>
#include <iostream>
using namespace std;
int a[1000010];
int qu[1000010];
int top=0;
int tail=0;
int main(){
int n;
int k;
scanf("%d %d",&n,&k);
for(int i=1; i<=n; i++){
scanf("%d",&a[i]);
}
tail=1,top=1;
qu[tail]=1;
//初始化队列
for(int i=2; i<=k; i++){ //找最小的
while(tail>0 && a[i]<a[qu[tail]]){
tail--;
}
tail++;
qu[tail]=i;
}
cout<<a[qu[top]]<<" ";
int l=1,r=k;
while(r<n){
if(qu[top]==l) top++;
r++;
l++;
while(tail>=top && a[r]<a[qu[tail]]){
tail--;
}
tail++;
qu[tail]=r;
cout<<a[qu[top]]<<" ";
}
cout<<endl;
tail=1,top=1;
qu[1]=1;
for(int i=2; i<=k; i++){ //找最大的
while(tail>0 && a[i]>a[qu[tail]]){
tail--;
}
tail++;
qu[tail]=i;
}
cout<<a[qu[1]]<<" ";
l=1,r=k;
while(r<n){
if(qu[top]==l) top++;
r++;
l++;
while(tail>=top && a[r]>a[qu[tail]]){
tail--;
}
tail++;
qu[tail]=r;
cout<<a[qu[top]]<<" ";
}
return 0;
}