单调栈以及应用

单调栈有以下两个性质:

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;  
}

这段程序维护了一个单调栈,来得到每个元素的左右边界。可以用来求最大矩形的面积,后面的一点程序就是计算一串数字在直方图中构成的最大矩形面积。

原文地址:https://www.cnblogs.com/mini-coconut/p/9108232.html