单调栈【总结】

一、单调栈是个啥??

顾名思义,单调单调,不就是数据有序的栈吗?所以单调栈分为两种:
1.单调递增栈
2.单调递减栈
让我们归纳下:栈内的元素,按照某种方式排序下(单调递增或者单调递减), 如果新入栈的元素破坏了单调(有序)性,就弹出栈内元素,直到满足单调(有序)性。
(这里所说的顺序是出栈顺序)
而且单调栈可以很方便地求出某个数的左边或者右边第一个比它大或者小的元素,总时间复杂度 O ( N ) O(N) O(N)。(这令人心动的复杂度)

二、如何维护单调栈??

(以单调递增栈为例)
进栈操作:每次入栈前先检验栈顶元素和进栈元素 ( x ) (x) (x)的大小,如果小于 x x x,就让 x x x直接入栈。如果栈顶元素大于等于 x x x,那么栈顶元素出栈,直到栈空或者栈顶元素小于x

举个栗子:

1 4 3 6 0
初始时刻栈空,1入栈。----------------------栈内元素(1)
4要进栈,4大于1,所以直接进栈.-----------栈内元素(1,4)
3要进栈,3小于4,4出栈,3进栈.-----------栈内元素(1 3)
6要进栈,6大于3,6直接进栈。-------------栈内元素(1,3,6)
0要入栈,1,3,6都出栈,-------------------栈内元素(0)

三、单调栈的伪代码

转载自:https://blog.csdn.net/lucky52529/article/details/89155694

stack<int> st;
for (遍历这个数组)
{
	if (栈空 || 栈顶元素大于等于当前比较元素)
	{
		入栈;
	}
	else
	{
		while (栈不为空 && 栈顶元素小于当前元素)
		{
			栈顶元素出栈;
			更新结果;
		}
		当前数据入栈;
	}
}

四、例题讲解

一、 Largest Rectangle in a Histogram

给出一个柱形统计图(histogram), 它的每个项目的宽度是1, 高度和具体问题有关。 现在编程求出在这个柱形图中的最大面积的长方形 ( n < = 1 e 5 ) (n<=1e5) n<=1e5)
在这里插入图片描述
例如:2, 1, 4, 5, 1, 3, 3
最大面积:8

分析

逐个考虑每个项目,那么如何得到每个项目被包含的长方形?
假设考虑到项目x,通过简单的思考,我们可以得出结论,x的左右两边的项目高度都不能比x低,那么如何求项目x的两边延伸长度?
假设我们建立一个单调递增栈,那么我们可以轻松地求得x左边比x小的第一个数的位置。
求x右边延伸相同处理

C o d e Code Code:

int top=0,s[maxn];
for(int i=1;i<=n;i++)
   while(top&&h[s[top]]>=h[i])
       --top;
   L[i]=(top==0?1:s[top]+1);
   s[++top]=i;

最大数

原题来自:JSOI 2008
给定一个正整数数列 a 1 , a 2 , a 3 . . . a n a_1, a_2, a_3...a_n a1,a2,a3...an,每一个数都在 0 ∼ p – 1 0∼p–1 0p1 之间。可以对这列数进行两种操作:
添加操作:向序列后添加一个数,序列长度变成 n + 1 n+1 n+1
询问操作:询问这个序列中最后 L L L个数中最大的数是多少。
程序运行的最开始,整数序列为空。写一个程序,读入操作的序列,并输出询问操作的答案。

分析:

单调栈+并查集
在这道题里,这需要写模板单调栈即可。接下来讲下并查集的思路:在将一个数插入单调队列时,我们可以将被删除的数的父亲标记为哪个新插入的数,在查找时只需要找到要查找的数的根,根上的数值即为答案。

C o d e : Code: Code:

#include<bits/stdc++.h>
using namespace std;
const int Maxn=200010;
struct node
{
    long long x;
    int y;
}a[Maxn];
int m,tot,cnt,f[Maxn];
long long d,t,x,num[Maxn];
char ch[3];
int find(int x)//并查集标记父亲
{
    if(x!=f[x])f[x]=find(f[x]);
    return f[x];
}
int main()
{
    scanf("%lld%lld",&m,&d);
    for(int i=1;i<=m;i++)
    {
        getchar();
        scanf("%s",ch);
        scanf("%lld",&x);
        if(ch[0]=='A')
        {
            x+=t;
            x%=d;//计算要插入元素的值
            tot++;
            num[tot]=x;
            f[tot]=tot;
            while(x>a[cnt].x&&cnt)//模板单调栈
            {
                f[a[cnt].y]=tot;
                cnt--;
            }
            a[++cnt].x=x;
            a[cnt].y=tot;
        }
        else
        {
            x=tot-x+1;
            int y=find(x);
            t=num[y];//记录询问操作的答案(后面取模用)
            printf("%lld\n",t);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值