单调栈以及单调队列入门

单调栈以及单调队列入门

一、单调栈
单调栈是指一个栈内部的元素具有严格单调性的一种数据结构,分为单调递增栈和单调递减栈。

其具有以下两个性质:
1,满足栈底到栈顶的元素具有严格单调性。
2,满足栈的先进后出特性,越靠近栈顶的元素越后出栈。

元素进栈过程:

对于一个单调递增栈来说,若当前进栈的元素为a,如果a<栈顶元素,则直接将a进栈。

如果a≥栈顶元素,则不断将栈顶元素出栈,直到满足a<栈顶元素。
![![在这里插入图片描述](https://img-blog.csdnimg.cn/20190830125043401.png?x-](https://img-blog.csdnimg.cn/20190830125057624.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2VtbW0x,size_16,color_FFFFFF,t_70
假设维护一个单调上升的栈。如果入栈元素小于栈顶那么就要开始pop。而pop掉的元素一定全都大于这个入栈元素。单调栈内的两个相邻元素a,b如果在原序列中不是相邻的,则意味着b的出现pop掉了中间的元素,因此这中间的元素必定都大于b。
一个元素的进入弹掉了一部分元素,由此求出最左延伸量。如果一个元素即将被pop,意味着终于出现了一个比它小的,就可以求出最右延伸量了。

时间复杂度分析
对于每个元素,其有且仅有一次插入,最多出现一次删除,故其时间复杂度为O(n)。

例题:hdu1506
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1506
题意:告诉一个直方图中每个小矩形的高度,求所包含的最大矩阵面积。
思路:既然要求最大的矩形面积,就需要求出每一个高度的最大矩形面积,在一个高度上,我们向左遍历遇到的第一个比该高度要小的坐标,则这个坐标为该高度的最大矩形的左坐标,右边同理得到该高度的最大矩形的右坐标,但是如果用朴素的暴力去求的话复杂度为O(N^2),会超时,如何进行优化呢,我们就需要用到单调栈来进行优化,因为我们知道单调递减栈是遍历的时候是越来越小的,所以满足我们的要求,这样我们就合并了一些区间,使得复杂度变为了O(N)。

代码如下:

#include<bits/stdc++.h>
#define ll long long
#define MAXN 100010
using namespace std;
int h[MAXN],l[MAXN],r[MAXN];
stack<int> s;
int main(){
	int n;
	ll area;
	while(cin>>n&&n) {
		area=0;
		for(int i=1;i<=n;i++) {
			cin>>h[i];
		}
		while(!s.empty()) {
			s.pop();
		}
		for(int i=1;i<=n;i++) {
			while(!s.empty()&&h[s.top()]>=h[i]) {
				s.pop();
			}
			if(s.empty()) {
				l[i]=0;
			}
			else {
				l[i]=s.top();
			}
			s.push(i);
		}
		while(!s.empty()) {
			s.pop();
		}
		for(int i=n;i>=1;i--) {
			while(!s.empty()&&h[s.top()]>=h[i]) {
				s.pop();
			}
			if(s.empty()) {
				r[i]=n;
			}
			else {
				r[i]=s.top()-1;
			}
			s.push(i);
		}
		for(int i=1;i<=n;i++) {
			area=max((ll)(r[i]-l[i])*h[i],area);
		}
		cout<<area<<endl;
	}
	return 0;
}
  

二、单调队列
单调队列与单调栈及其相似,把单调栈先进后出的性质改为先进先出既可。

元素进队列的过程对于单调递增队列。
对于一个元素a,如果a>队尾元素,那么直接将a扔进队列,如果a≥队尾元素,则将队尾元素出队列,直到满足 a>队尾元素即可。
注意:一般的队列不支持队尾删除,所以一般用双端队列dequeue,但是用数组模拟就不存在这样的问题
时间复杂度分析
对于每个元素,其有且仅有一次插入,最多出现一次删除,故其时间复杂度为O(n)。

例题:hdu 3530
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3530
题意:给定一段区间,求一个最大的子区间的长度(该区间必须满足此区间的最大值和最小值之差小于等于k大于等于m)
思路:既然要求最大的子区间,就需要求出每一个区间的最大值和最小值,在一个区间中,如果该区间的最大值和最小值满足题目中的条件,就和ans比较取最大值(ans初始值为0)。但是如果用朴素的暴力去求的话复杂度为O(N^2),会超时,如何进行优化呢,我们就需要用到单调队列来进行优化,因为我们知道单调队列是遍历的时候是越来越小的,所以满足我们的要求,这样我们就合并了一些区间,使得复杂度变为了O(N)。

代码如下:

#include<bits/stdc++.h>
#define ll long long
#define MAXN 200010
using namespace std;
int a[MAXN];
deque<int> q1,q2;
int main(){
	int n,m,k;
	while(cin>>n>>m>>k) {
		int ans=0;
		for(int i=1;i<=n;i++) {
			cin>>a[i];
		}
		while(!q1.empty()) {
			q1.pop_back();
		}
		while(!q2.empty()) {
			q2.pop_back();
		}
		int flag=0;
		for(int i=1;i<=n;i++) {
			while(!q1.empty()&&a[i]>a[q1.front()]) {
				q1.pop_front();
			}
			q1.push_front(i);
			while(!q2.empty()&&a[i]<a[q2.front()]) {
				q2.pop_front();
			}
			q2.push_front(i);
			while(!q1.empty()&&!q2.empty()&&a[q1.back()]-a[q2.back()]>k) {
				if(q1.back()>q2.back()) {
					flag=q2.back();
					q2.pop_back();
				}
				else {
					flag=q1.back();
					q1.pop_back();
				}
			}
			if(!q1.empty()&&!q2.empty()&&a[q1.back()]-a[q2.back()]>=m) {
					ans=max(ans,i-flag);
				}
		}
		cout<<ans<<endl;
	}
	return 0;
}
  

——————后序补充中。。。。。

在Python中,单调栈单调队列是两种不同的数据结构。单调栈是一个栈,它的特点是栈内的元素是单调的,可以是递增或递的。在构建单调栈时,元素的插入和弹出都是在栈的一端进行的。与此类似,单调队列也是一个队列,它的特点是队列内的元素是单调的,可以是递增或递的。在构建单调队列时,元素的插入是在队列的一端进行的,而弹出则是选择队列头进行的。 单调队列在解决某些问题时,能够提升效率。例如,滑动窗口最大值问题可以通过使用单调队列来解决。单调队列的结构可以通过以下代码来实现: ```python class MQueue: def __init__(self): self.queue = [] def push(self, value): while self.queue and self.queue[-1 < value: self.queue.pop(-1) self.queue.append(value) def pop(self): if self.queue: return self.queue.pop(0) ``` 上述代码定义了一个名为MQueue的类,它包含一个列表作为队列的存储结构。该类有两个方法,push和pop。push方法用于向队列中插入元素,它会删除队列尾部小于插入元素的所有元素,并将插入元素添加到队列尾部。pop方法用于弹出队列的头部元素。 总结来说,单调栈单调队列都是为了解决特定问题而设计的数据结构。单调栈在构建时元素的插入和弹出都是在栈的一端进行的,而单调队列则是在队列的一端进行的。在Python中,可以通过自定义类来实现单调队列的功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值