单调栈有以下两个性质:
1、若是单调递增栈,则从栈顶到栈底的元素是严格递增的。若是单调递减栈,则从栈顶到栈底的元素是严格递减的。
2、越靠近栈顶的元素越后进栈。
单调栈与单调队列不同的地方在于栈只能在栈顶操作,因此一般在应用单调栈的地方不限定它的大小,否则会造成元素无法进栈。
元素进栈过程:对于单调递增栈,若当前进栈元素为e,从栈顶开始遍历元素,把小于e或者等于e的元素弹出栈,直接遇到一个大于e的元素或者栈为空为止,然后再把e压入栈中。对于单调递减栈,则每次弹出的是大于e或者等于e的元素。
单调栈解决的是以某个值为最小(最大)值的最大区间。
实现方法是:求最小值(最大值)的最大区间,维护一个递增(递减)的栈;当遇到一个比栈顶小的值的时候开始弹栈,弹栈停止的位置到这个值的区间即为此值左边的最大区间;同时,当一个值被弹掉的时候也就意味着比它更小(更大)的值来了,也可以计算被弹掉的值得右边的最大区间。
具体方法就是:因为求小值的大区间,采用栈底向栈顶递增的方式维护单调栈。
起始时,元素的左右区间均为自身位置。
当遇到栈顶元素大于当前将要入栈的数值时候,将栈顶元素的左区间赋值给入栈元素的左区间,同时将入栈元素当前位置赋值给栈顶元素的右区间,之后栈顶元素出栈,继续判断入栈元素跟栈顶元素的大小。(总的来说就是谁让你出栈,谁就是你的右区间,你让谁出栈,谁的左区间就是你的左区间)
当全部元素入栈成功后,最后在单调栈中的元素为由小到大排列,只需要将最后入栈的栈顶元素位置保存下来,将该位置赋值给栈内剩余元素的右区间即可。
struct Node { int value; int left; int right; }; int main() { int n,j; cin >> n; int maxarea=0; vector<Node> h(n+1); stack<int> monotone; for (int i = 1; i <= n; i++) { cin >> h[i].value; h[i].left=h[i].right=i; } for(int i=1;i<=n;i++) { while(!monotone.empty() && h[monotone.top()].value > h[i].value) { h[monotone.top()].right=i-1; h[i].left=h[monotone.top()].left; monotone.pop(); } monotone.push(i); } if(!monotone.empty()) j=monotone.top(); while(!monotone.empty()) { h[monotone.top()].right=j; monotone.pop(); } for(int i=1;i<=n;i++) { int area = h[i].value*(h[i].right-h[i].left+1); maxarea=maxarea<area ? area : maxarea; //cout<<h[i].value<<":"<<h[h[i].left].value<<" "<<h[h[i].right].value<<endl; } cout<< maxarea<<endl; return 0; }
这段程序维护了一个单调栈,来得到每个元素的左右边界。可以用来求最大矩形的面积,后面的一点程序就是计算一串数字在直方图中构成的最大矩形面积。