单调队列_入门题目滑动窗口

Acwing_154 滑动窗口
题目链接:https://www.acwing.com/problem/content/156/

好久没有做单调队列的题目了,正好碰到了重新复习一波;

对这道题目而言;我们首先考虑朴素做法,直接暴力去遍历每次拆窗口的数值,时间复杂度为:0(n * k);
在暴力的过程中,我们发现;对于一个这样一个窗口:
单调队列_入门题目滑动窗口_第1张图片
当-3,存在的时候,-1和3是永远不可能是最小数的;可以得到一个这样的结论,当一个数出现,如果说这个数比前面出现过的数小,那么,在后面窗口不断进行滑动的过程中,前面的数字就是无效的;所以这里我们需要考虑维护两个东西,一个是数字本身的单调性,一个是数字本身锁出现的顺序;
思路:当一个新数字出现的时候,我们首先需要判断当前的队头元素是否已经不在当前的窗口长度中,如果不在则将其出队;然后我们再对队列剩下的数字进行判断,并且此时从队尾进行判断,若队列中的数大于当前即将进入的数字,那么就将当前队尾的数字出队伍(此时是一个双向队列)直到队列为空或者找到当前队尾的元素的数值比当前即将要进来的数字更小就停止出队;最后将这个数字进队;

贴上代码:

#include 
#include 
#include 
#include 

using namespace std;

const int maxn = 1e6 + 5;

int a[maxn];
int q[maxn], hh = 0, tt = -1;     //hh:队头, tt:队尾 

int main(void) {
//	freopen("in.txt", "r", stdin);
	int n, k; 
	scanf("%d%d", &n, &k);
	for(int i = 1; i <= n; i ++) scanf("%d", &a[i]);
	
	//输出滑动窗口中的最小值 
	for(int i = 1; i <= n; i ++) {
		//首先判断对头的元素是否需要出队,由于窗口每滑动一次只会对一个元素进行操作,所以此处只需要if
		if(hh <= tt && i - k + 1 > q[hh]) hh ++;   //如果当前队列中有元素并且当前窗口中初始的下标已经大于了此时队头元素的下标,则出队
		while(hh <= tt && a[q[tt]] >= a[i]) tt --;      //如果当前队尾的元素的大小大于了a[i],那么直接将队尾元素出队即可;此时的单调队可以看成是一个双向队列
		q[++ tt] = i;     //当前元素的下标入队列
		if(i >= k) printf("%d ", a[q[hh]]); 
	}
	
	puts("");
	
	//输出滑动窗口中的最大值
	hh = 0, tt = -1;
	for(int i = 1; i <= n; i ++) {
		if(hh <= tt && i - k + 1 > q[hh]) hh ++;
		while(hh <= tt && a[q[tt]] <= a[i]) tt --;
		q[++ tt] = i;
		if(i >= k) printf("%d ", a[q[hh]]);		
	} 
	
//	fclose(stdin); 
	return 0;
}

你可能感兴趣的:(#)