leetcode - [栈] -柱状图中的最大矩形(84)

1、问题描述

给定n个非负整数,用来表示柱状图中每个柱子的高度。每个柱子彼此相邻,且宽度为1.
求柱状图中能勾勒出的最大面积。
在这里插入图片描述
以上是柱状图的示例,其中每个柱子的宽度为 1,给定的高度为 [2,1,5,6,2,3]。
在这里插入图片描述
图中阴影部分为所能勾勒出的最大矩形面积,其面积为 10 个单位。

2、解题思路

方法1暴力法。在柱子 i i i和柱子 j j j构成的区间中,最大的矩形面积为区间长度*区间内最矮柱子的高度,即 ( j − i + 1 ) × m i n { h e i g h t } (j-i+1) \times min\{height\} (ji+1)×min{height},因此我们可以枚举所有的区间,然后找到每个区间的最小值,并计算该区间内最大矩形的面积。最后这些面积中选择最大的那个。
这种方法的时间复杂度为 O ( n 3 ) O(n^3) O(n3)。枚举所有的区间需要 O ( n 2 ) O(n^2) O(n2)的时间复杂度,在区间中寻找最小值需要 O ( n ) O(n) O(n)的时间复杂度。空间复杂度为 O ( 1 ) O(1) O(1)

方法2暴力法的优化。对于每个区间,上述暴力法都会扫描一遍以找到最小值,但其实这不是必须的,比如对于 [ 2 , 0 , 1 ] [2,0,1] [2,0,1] [ 2 , 0 , 1 , 3 ] [2,0,1,3] [2,0,1,3]这两个区间,由于后一个区间包含前一个区间,故后一个区间的最小值很有可能和前一个区间相等,因此在求后一个区间的最小值时,只需将后一个区间拓展的元素和前一个区间的最小值比较,如果小于则更新该区间的最小值。
这种方法的时间复杂度为 O ( n 2 ) O(n^2) O(n2),空间复杂度为 O ( 1 ) O(1) O(1)

方法3单调栈。我们可以换个角度来看这个问题。以 [ 1 , 2 , 4 , 5 , 3 ] [1,2,4,5,3] [1,2,4,5,3]为例:
对于高度为4的柱子而言,如何计算以4为高度的矩形的最大面积?
很显然,我们需要找到柱子4的左、右边界。然后计算它的高度与左右边界形成的区间的乘积。
什么是它的左右边界? 左边第一个高度小于它的柱子就是它的左边界,右边第一个高度大于它的柱子就是它的右边界。
像这种涉及 n e x t g r e a t e r nextgreater nextgreater n e x t s m a l l e r nextsmaller nextsmaller的问题,都可以使用单调栈来解决。维持一个单调递增栈,对于栈顶元素 h e i g h t [ s ( t o p ) ] height[s(top)] height[s(top)],次栈顶元素即为它的左边界(即左边第一个小于它的元素),如果当前遍历元素 h e i g h t [ c u r r e n t ] height[current] height[current]大于栈顶元素,那么当前元素即为它的左边界,这时可以求得以栈顶元素作为高的矩形最大面积为:
a r e a = h e i g h t [ s ( t o p ) ] × ( c u r r e n t − s ( t o p − 1 ) − 1 ) area = height[s(top) ] \times (current - s(top - 1) - 1) area=height[s(top)]×(currents(top1)1)
注意,当遍历完最后一个元素时,
对于栈中剩下的每个元素,以其为高构成的最大矩形面积为:
a r e a = h e i g h t [ s ( t o p ) ] × ( h e i g h t . l e n g t h − s ( t o p − 1 ) − 1 ) area = height[s(top)] \times(height.length - s(top-1)-1) area=height[s(top)]×(height.lengths(top1)1)
对于该算法的动态演化过程,可参考:例子动态演示
这种方法的时间复杂度为 O ( n ) O(n) O(n),空间复杂度为 O ( n ) O(n) O(n).

3、代码实现

class Solution {
public:
    int largestRectangleArea(vector<int>& heights) {
        int len = heights.size();
        int maxarea = 0;
        stack<int> s;
        s.push(-1);
        for(int i = 0; i < len; i++){
            while(s.top() != -1 && heights[i] < heights[s.top()]){
                int top = s.top();
                s.pop();
                maxarea = max(maxarea, heights[top] * (i - s.top() - 1));
            }
            s.push(i);
        }
        while(s.top() != -1){
            int top = s.top();
            s.pop();
            maxarea = max(maxarea, heights[top] * (len - s.top() - 1));
        }
        return maxarea;
        
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Albert_YuHan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值