欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

week5 D-滑动窗口 单调队列

程序员文章站 2022-05-04 17:15:49
...

题意:

给定一个数组(大小为n),以及一个窗口(大小为k)。窗口在数组上从开始到结尾移动,输出每个位置的窗口里的最大值和最小值。
如图:
week5 D-滑动窗口 单调队列

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;
}