单调栈的运用 by Lucas Yang

单调栈的介绍参考:

单调栈的介绍以及一些基本性质

使用单调栈可以找到元素向左遍历第一个比他小的元素,也可以找到元素向左遍历第一个比他大的元素。

1.当单调栈中的元素是单调递增的时候,根据上面我们从数组的角度阐述单调栈的性质的叙述,可以得出:

(1).当a > b 时,则将元素a插入栈顶,新的栈顶则为a

(2).当a < b 时,则将从当前栈顶位置向前查找(边查找,栈顶元素边出栈),直到找到第一个比a小的数,停止查找,将元素a

插入栈顶(在当前找到的数之后,即此时元素a找到了自己的“位置”)

2.当单调栈中的元素是单调递减的时候,则有:

(1).当a < b 时,则将元素a插入栈顶,新的栈顶则为a

(2).当a > b 时,则将从当前栈顶位置向前查找(边查找,栈顶元素边出栈),直到找到第一个比a大的数,停止查找,将元素a

插入栈顶(在当前找到的数之后,即此时元素a找到了自己的“位置”)

常见的题目:

leetcode :寻找直方图最大矩形,求最大子矩阵的大小,平面矩形最大蓄水量

1 leetcode 84. Largest Rectangle in Histogram 寻找直方图最大矩形

Given n non-negative integers representing the histogram's bar height where the width of each bar is 1, find the area of largest rectangle in the histogram.


Above is a histogram where width of each bar is 1, given height = [2,1,5,6,2,3].


The largest rectangle is shown in the shaded area, which has area = 10 unit.

For example,
Given heights = [2,1,5,6,2,3],
return 10.


(1)这个题目可以使用动态规划

考虑dp[i][j]表示ij范围内的最低的柱子,通过动态递归一次求得所有区间的最小柱子。并且保存全局最大变量。

进一步,可以空间压缩成一维动态规划。但是这种方法是n方复杂度。

(2)使用单调栈 复杂度 n  讲解参见《程序员代码面试指南》求最大子矩阵的大小

题目要求寻找直方图的最大矩形。

对于一个柱子,考察以他的高扩展的最远边界。如图中的5所示,左边没发扩展,右边可以扩展到6。所以,以他为高的柱子的矩形面积是5*2=10。依次找到所有柱子尽量拓展后的面积,取最大值即可。

单调栈解决的是,在一次遍历下,找到所有节点最左和最右的可扩展边界。

这个题目要求找到当前柱子,左边和右边第一个比他小的柱子的位置,中间的区域长度乘以当前柱子的高度就是当前的面积。

使用单调递增栈,规则如下:

(1)栈为空时,将当前元素压入栈。

(2)如果当前位置i的高度height[i]大于栈顶元素时,将该元素下标进栈。确保栈中元素从栈顶到栈底是依次减小的。

(3)如果当前位置i的高度小于等于栈顶元素,依次弹出栈,直到栈为空或者栈顶位置的高度比height[i]大时为止。

(4)在(3)循环体内部同时做下面的操作:当前位置为i,弹出元素为j,栈顶元素为k.那么,j位置的最右边界时i,最左边界为k,矩形面积是(i-k-1)*height[j](矩形面积不包括边界)。如果栈中没有元素了,说明可以扩充到左边的全部,k取-1。

(5)如果完全遍历完成。栈中还有元素。那么依次弹出栈。并把右边界当成是右边最大位置的下一个。其他操作与(4)一样。

有一点:出栈时,要小于等于,是因为如果有重复的元素,不加等于号就会出错。而加了等于号时,会出现下面的情况:对于相等的值,可能只有最右面的值的面积能够求对。因为,如果两个重复值之间都是比他们大的值,那么左边重复值的右边界在遍历到右边重复值时确定为右边边界。而右边边界应该被计算进来,而且右边边界右边可能还有符合要求的值。左边边界不会计算错。

但是如果弹出元素是最右边的重复值时,计算没有问题。这个时候,左边的重复值早已经弹出了。而左边重复值的左边界还没有弹出。

class Solution {
public:
    int largestRectangleArea(vector<int>& re) {
       int n=re.size();
       stack<int> res;
        int sum=0;
        for(int i=0;i<n;i++)
        {
            while(!res.empty()&&re[i]<=re[res.top()])
            {
                int j=res.top();
                res.pop();
                int k=res.empty()?-1:res.top();
                sum=max(sum,(i-k-1)*re[j]);
            }
            res.push(i);
        }
        while(!res.empty())
        {
            int j=res.top();
            res.pop();
            int k=res.empty()?-1:res.top();
            sum=max(sum,(n-k-1)*re[j]);
        }
        return sum;
    }
};

2 85. Maximal Rectangle 最大子矩阵大小

Given a 2D binary matrix filled with 0's and 1's, find the largest rectangle containing only 1's and return its area.

For example, given the following matrix:

1 0 1 0 0
1 0 1 1 1
1 1 1 1 1
1 0 0 1 0
Return 6.

给定一个矩阵,返回所有元素都是1的矩形的最大面积

思路分析:

以每一层为底,依次求得每个位置的最大高度。这样,这一层的最大子矩阵,就是对应的最大直方图的最大矩形面积。转换为第一个题目。使用的还是单调递增栈。

class Solution {
public:
    int maximalRectangle(vector<vector<char>>& matrix) {
        if(matrix.empty())
        return 0;
        int result=0;
        vector<int> histogram(matrix[0].size(),0);
        for(int i=0;i<matrix.size();i++)
        {
            for(int j=0;j<matrix[0].size();j++)
            {
                if(matrix[i][j]=='0') histogram[j]=0;
                else histogram[j]+=1;
            }
            result=max(result,largestRectangleArea(histogram));
        }
        return result;
    }
    int largestRectangleArea(vector<int>& re) {
       int n=re.size();
       stack<int> res;
        int sum=0;
        for(int i=0;i<n;i++)
        {
            while(!res.empty()&&re[i]<=re[res.top()])
            {
                int j=res.top();
                res.pop();
                int k=res.empty()?-1:res.top();
                sum=max(sum,(i-k-1)*re[j]);
            }
            res.push(i);
        }
        while(!res.empty())
        {
            int j=res.top();
            res.pop();
            int k=res.empty()?-1:res.top();
            sum=max(sum,(n-k-1)*re[j]);
        }
        return sum;
    }
};






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值