单调栈是个什么栈
没错单调栈其实就是个单调的栈,在存入栈的时候呈现出单调递增(递减)的数据结构,弹出不符合单调顺序的栈顶元素,直到遇到符合单调结构,如此存入形成的栈就是单调栈。
单调栈的具体工作过程:
1、新元素加入栈前,会在栈顶端把破坏栈单调性的元素都删除,直到栈为空或者栈满足单调性才能加入新元素;
2、单调栈是 O(N) 级的时间复杂度,所有元素只会进入栈一次,并且出栈后再也不会进栈;
3、单调栈可以找到元素向左遍历第一个比它小(大)的元素,也就是说在元素进栈前它向左拓展的区间已经确定,在出栈前它能向右拓展的区间也能确定(左区间好理解,仔细体会右区间的确定,若该元素至遍历结束后也未出栈,那么就是说在原数组中,该元素的右方向没有一个元素可以比它大/小,那么该元素的右边界就是原数组的大小(就是没有右边界),否则它的右边界就是令它出栈的元素)。
单调栈有什么用
举一个经典的题目来显现单调栈算法的好处
题目描述
直方图是由在公共基线处对齐的一系列矩形组成的多边形。
矩形具有相等的宽度,但可以具有不同的高度。
例如,图例左侧显示了由高度为2,1,4,5,1,3,3的矩形组成的直方图,矩形的宽度都为1:
通常,直方图用于表示离散分布,例如,文本中字符的频率。
现在,请你计算在公共基线处对齐的直方图中最大矩形的面积。
图例右图显示了所描绘直方图的最大对齐矩形。
输入格式
输入包含多个测试用例
每行先输入一个 n表示组成直方图的矩形个数,后面跟 n个整数表示每个矩形的高度
当 n为 0时结束。
朴素暴力
首先考虑暴力做法,以每个矩形的高度为准,向两边扩展,直到遇到比它矮的为止
如图所示,记录每个矩形向两侧扩展的边界 [l,r][l,r]
它扩展出的矩形面积为 s=(r−l+1)∗hs=(r−l+1)∗h
最优解会在这些扩展出的矩形中产生。
时间复杂度 :O (n^2)
每个矩形向两侧扩展的最大宽度为矩形个数 n,共进行 n 次这样的操作
单调栈优化
在计算每个矩形可以扩展的左边界时,可以发现有一些矩形是可以不考虑的
如图所示,由于2号矩形的存在,在计算2右边的矩形的左边界时,可以不考虑1号矩形。
高度高于2号的矩形会被2卡住,高度小于等于2号的也必然小于等于1号。
观察可知,在计算左边界时,靠左的且较高的矩形可以省略,因此可以用单调栈优化。
S[head]作为栈顶元素下标,当满足**h[s[head]]>= h[i]**时,弹出栈顶。
#include<stdio.h>
int h[100010],s[100010],l[100010],r[100010],n;
void findleftarea(int l[])
{
int head=-1,i;
s[++head]=0;
h[0]=h[n+1]=-1;
for(i=1;i<=n;i++)
{
while(h[s[head]]>=h[i])
head--;
l[i]=s[head];
s[++head]=i;
}
}
void findrightarea(int r[])
{
int head=-1,i;
s[++head]=n+1;
h[0]=h[n+1]=-1;
for(i=n;i>=1;i--)
{
while(h[s[head]]>=h[i])
head--;
r[i]=s[head];
s[++head]=i;
}
}
int main()
{
while(~scanf("%d",&n),n)
{
for(int i=1;i<=n;i++)
scanf("%d",&h[i]);
findleftarea(l);
findrightarea(r);
long long ans=0;
for(int i=1;i<=n;i++)
ans=ans>=((long long)r[i]-l[i]-1)*h[i]?ans:((long long)r[i]-l[i]-1)*h[i];
printf("%lld\n",ans);
}
}