84. Largest Rectangle in Histogram 柱状图中的最大矩形

LeetCode传送门

求柱状图(Histogram)中的最大矩形(Largest Rectangle),是一道ACM比赛题,又是一道Leetcode题,还是一道经常被问的面试题。如下图所示,可以将柱状图表示为一个数组[2,1,5,6,2,3],每个柱子的宽度假设是一个单位。




求出的最大的矩形应该如下斜线所示,大小是10。




最容易想到的解法是brute force

class Solution:
    def largestRectangleArea(self, heights):
        """
        :type heights: List[int]
        :rtype: int
        """
        maxH = 0
        for i in range(len(heights)):
            left = i
            while left >= 0 and heights[left] >= heights[i]:
                left -= 1
            right = i
            while right <= len(heights) - 1 and heights[right] >= heights[i]:
                right += 1
            temp = (right - left - 1) * heights[i]
            if temp > maxH:
                maxH = temp
        return maxH

A = Solution()
res = A.largestRectangleArea([2,1,5,6,2,3])
print(res)

然而,最神奇的基于堆栈的O(n)解法基本思路(如下图):从第一个柱子(编号1)开始,找出所有的后一个比前一个上手的柱子(从编号1到13),直到遇到一个高度下降的柱子(编号14)。而且把下降之前的编号(从1到13)推入到一个堆栈中。然后计算栈顶编号柱子的高度(所有A1A2线之上的柱子们,从编号3到13)比下降柱子(14)高的所有矩形的面积(因为14不可能和比它高的柱子组成更大的矩形),保留最大的。从栈顶开始,比如,13,12-13,...,8-13,7-13,...,直到3-13。但是14能参与比它矮的较远的编号2(2-14,就是B1B2)和编号1(1-14,就是C1C2)组成矩形。可以看出,堆栈中保留了到当前为止,所有将要参与可能矩形计算的所有柱子的编号。有了这些编号,我们可以计算所有可能的矩形的面积,从而求出最大的。注意了,堆栈中的编号的柱子高度时递增的!




用python实现的代码看上去很简洁。

class Solution:
    def largestRectangleArea(self, heights):
        stack = []
        index = 0
        heights.append(-1)
        maxH = 0
        while index < len(heights):
            if len(stack) == 0 or heights[stack[-1]] <= heights[index]:
                stack.append(index)
                index += 1
            else:
                area = 0
                top = stack.pop()
                if len(stack) == 0:
                    area = heights[top] * index
                else:
                    area = heights[top] * (index - stack[-1] - 1)
                if area > maxH:
                    maxH = area
        return maxH

A = Solution()
res = A.largestRectangleArea([2,1,5,6,2,3])
print(res)

假设用例柱状图数组是[2,1,5,6,2,3]。具体解释代码的运行过程,有助于理解。


初始时堆栈为空,将编号为0推入堆栈(是推入编号,不是高度,堆栈中保存的是到当前为止可以参与矩形的最左边柱子),这时的堆栈是[0]。移动到下一个索引编号1。



这时栈顶中编号的柱子(编号0)高于当前正在处理的编号1的柱子的高度,需要弹出堆栈,计算第一个矩形的面积,就是下图的蓝色矩形,这是当前的最大矩形。然后移动到下一个柱子。




这是堆栈为空,将当前柱子的编号1推入堆栈[1]。由于编号1,2,3的柱子的高度是递增的,所以持续推入堆栈[1,2,3]。直到遇到编号为4的柱子,高度下降。这是,要开始弹出堆栈和计算可能的所有当前矩形的面积了。



首先弹出栈顶元素,编号3,这是堆栈为[1,2],然后计算下图绿色矩形面积,看是不是当前最大矩形。




这时栈顶编号2柱子比当前index=4的柱子高,接着弹出栈顶元素2,此时堆栈为[1],然后计算粉色矩形的面积,看是不是当前最大。



这时栈顶编号1的柱子的高度比当前index=4的柱子矮,那么需要将编号4,5,推入堆栈,这时堆栈为[1,4,5],直到遇见最后一个高度为-1的标志柱子。




这时需要计算以堆栈中[1,4,5](注意了,堆栈中编号柱子的高度时递增的)所有编号为最左边的矩形的面积。弹出编号5,计算蓝绿色矩形面积;接着弹出4,计算黄色矩形面积;最后弹出编号1,计算深红色矩形面积。到此为止,完成了所有的计算,找出了最大矩形。




从以上代码流程可以看到,堆栈中的元素的编号始终保持高度是递增,当遇到高度下降的柱子时,依次弹出并计算以栈顶编号柱子为最左边的矩形的面积,直到堆栈中编号的柱子的高度小于当前正在处理的柱子。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值