单调栈以及单调队列入门
一、单调栈
单调栈是指一个栈内部的元素具有严格单调性的一种数据结构,分为单调递增栈和单调递减栈。
其具有以下两个性质:
1,满足栈底到栈顶的元素具有严格单调性。
2,满足栈的先进后出特性,越靠近栈顶的元素越后出栈。
元素进栈过程:
对于一个单调递增栈来说,若当前进栈的元素为a,如果a<栈顶元素,则直接将a进栈。
如果a≥栈顶元素,则不断将栈顶元素出栈,直到满足a<栈顶元素。
假设维护一个单调上升的栈。如果入栈元素小于栈顶那么就要开始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;
}
——————后序补充中。。。。。