单调栈与单调队列

单调栈

常见模型:给定一个序列,求一下每一个数的左边离他最近的且比它小的数在什么地方。

存在逆序的关系,答案一定是右边的那个

#include <iostream>

using namespace std;

int n;
int stk[100000 + 10];
int tt;

int main(){
	cin >> n;
	for(int i = 1; i <= n; i++){
		int x;
		cin >> x;
		while(tt && stk[tt] >= x) tt--;
		if(tt){
			cout << stk[tt] << " ";
		}
		else{
			cout << -1 << " ";
		}
		
		stk[++tt] = x;
	}
	return 0;
}

单调队列

解决滑动窗口问题

可以解决区间内,每个长度为k的极值

因为每个区间的重合度实际上是非常高的,如果暴力的话会非常浪费时间

就对每个区间维护一个单调队列

这样维护一个队列,使得每个队列都是维护的它的单调性

那么只需要每一次更换区间的时候,移动到下一个区间进行维护,这时候需要判断队头元素是不是上一个区间的数,如果是需要剔除

因为每个区间都是维护一个单调递增的序列(这里举个单调递增的例子),那么每个区间的最小值就显然是这个单调队列的队头元素

所以是维护单调递增队列是取得最小值,维护单调递减队列是取得最大值

每个数最多被加一次删一次,n个元素就是n次,所以复杂度是O(N)O(N)O(N)

struct queue_in{ // queue_increase
	int head;
	int tail;
	
	int q[N];
	
	queue_in(){
		head = 1;
		tail = 0;
		memeset(q, 0, sizeof(q));
	}
	
	void init(){
		head = 1;
		tail = 0;
		memeset(q, 0, sizeof(q));
	}
	
	void push(int x){
		while(head <= tail && q[tail] > x) // 先弹出不合法的元素,要注意队列不为空才能进行
			tail--;
		q[tail++] = x;
	}
	
	int front(){
		return q[head];
	}
	
	void pop(){
		head ++;
	}
	
	int size(){
		return tail - head + 1;
	}
}

滑动窗口

#include <iostream>
#include <cstdio>
#include <algorithm>

using namespace std;

const int N = 1000000 + 10;

int n, k;
int a[N];
int q[N];

int main(){
	cin >> n >> k;
	for(int i = 1; i <= n; i++){
		cin >> a[i];
	}
	
	int hh = 1;
	int tt = 0;
	
	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) cout << a[q[hh]] << " ";
	}
	cout << endl;
	
	hh = 1;
	tt = 0;
	memset(q, 0, sizeof(q));
	
	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) cout << a[q[hh]] << " ";
	}
	cout << endl;
	
	return 0;
}

由此我们可以引申出来很多结论,这时候这个区间是有单调性的,由于单调性的特性,我们可以考虑在实际问题中,怎么运用单调性来进行二分等等优化操作

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值