84. 柱状图中最大的矩形/C++

给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。

求在该柱状图中,能够勾勒出来的矩形的最大面积。

在这里插入图片描述

以上是柱状图的示例,其中每个柱子的宽度为 1,给定的高度为 [2,1,5,6,2,3]。

在这里插入图片描述

图中阴影部分为所能勾勒出的最大矩形面积,其面积为 10 个单位。

示例:

输入: [2,1,5,6,2,3]

输出: 10

这道题最好的做法是是使用单调栈,只要遍历一次就可求出最大面积。

一、如何求最大矩形面积

现在我们从头开始讲,如果要求只能遍历一次,那么如何求最大面积?
我想到一个思路,那就是先把能完全包含各个柱状图的矩形的最大面积求出来,然后求出其中最大值即可。以例题来说就是

  • 能完全覆盖第0个柱子的最大矩形
    能完全覆盖第0个柱子的矩形

  • 能完全覆盖第1个柱子的最大矩形
    在这里插入图片描述

  • 能完全覆盖第2个柱子的最大矩形
    在这里插入图片描述

  • 能完全覆盖第3个柱子的最大矩形
    在这里插入图片描述

  • 能完全覆盖第4个柱子的最大矩形
    在这里插入图片描述

  • 能完全覆盖第5个柱子的最大矩形
    在这里插入图片描述
    如此这般,就能够覆盖所有分支而又不遗漏,将这6个矩形的面积比较下就知道最大面积了。

二、如何求以某个柱子为高的最大矩形

在这里插入图片描述
我们就以例题中第4个,高为2的柱子举例好了。
矩形的面积=高*宽。
我们很高兴的发现,在这个分支情况下,我们已经知道高为2了,那么宽度如何求呢?
通过观察,我们发现矩形的左边沿是左边第一个高比2小的柱子,右边沿是右边第一个高比2小的柱子(将高为3的柱子的右面看作还有一个高为0的柱子)
如此它的宽度是6-(1+1)=4

这时可能你已经一头雾水了,6是啥?1是啥?为什么要加1?
如果你这么问,我打赌你肯定是如下图这么想的。
在这里插入图片描述
如果你将柱子的左下角对应序号的话,或许会好一些。
在这里插入图片描述
我们说了矩形的左边沿是第1个柱子的右边,那可不就是(1+1)了吗?
矩形右边沿是第6个柱子的左边,直接就是6.
宽度自然就是6-(1+1)=4了。

注意:基于各个高度的最大矩形是在出栈的时候计算的,因此必须要让所有高度都出栈。这里是利用单调栈的性质让其全部出栈,即在原始数组后添一个0.

三、如何寻找左右边沿

我们已经说了,左边沿是左边第一小与本柱子高的柱子的右边,右边沿也是同理。
这正好可以用单调栈。
当第i个柱子进栈时,如果栈顶柱子(此处记作柱子A)的高度低于或等于第i个柱子,则第i个柱子进栈;
如果高于第i个柱子,则出栈,并计算以柱子A为高的矩形最大面积。

  • 高度:就是柱子A的高度
  • 右边沿:正好是i(由于单调栈的性质,第i个柱子就是右边第一个矮于A的柱子)
  • 左边沿:单调栈中紧邻A的柱子。(如果A已经出栈,那么左边沿就是A出栈后的栈顶)而且是该柱子的右边,所以要+1.

因此,完全覆盖第index个柱子的最大矩形的面积如下(stk是单调栈)

maxArea=heights[index]*(i - (stk.top() +1))

还有一种情况。当A出栈后,单调栈为空时,那就是说明,A的左边没有比它矮的。左边沿就可以到0.

maxArea=heights[index]*(stk.empty()? i:(i - stk.top() -1)))

四、代码

int largestRectangleArea(vector<int>& heights) {
     heights.push_back(0);
     stack<int> stk;
     int maxArea = 0;
     for(int i = 0;i<heights.size();i++)
     {
         while(!stk.empty() && heights[i]<heights[stk.top()])
         {
             int top= stk.top();
             stk.pop();
             maxArea = max(maxArea,heights[top]*(stk.empty()? i:(i - stk.top() -1)));
         }
         stk.push(i);
     }
     return maxArea;
}
  • 125
    点赞
  • 102
    收藏
    觉得还不错? 一键收藏
  • 29
    评论
评论 29
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值