【LeetCode】84. 柱状图中最大的矩形(单调栈)

1. 题目描述

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

示例:

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

2. 解题思路

其实可以把这个想象成锯木板,如果木板都是递增的那我很开心,如果突然遇到一块木板 i i i 矮了一截,那我就先找之前最戳出来的一块(其实就是第 i − 1 i-1 i1块),计算一下这个木板单独的面积,然后把它锯成次高的,这是因为我之后的计算都再也用不着这块木板本身的高度了。再然后如果发觉次高的仍然比现在这个 i i i 木板高,那我继续单独计算这个次高木板的面积(应该是第 i − 1 i-1 i1 i − 2 i-2 i2 块),再把它俩锯短。直到发觉不需要锯就比第 i i i 块矮了,那我继续开开心心往右找更高的。当然为了避免到了最后一直都是递增的,所以可以在最后加一块高度为 0 0 0 的木板。

这个算法的关键点是把那些戳出来的木板早点单独拎出来计算,然后就用不着这个值了。

参考:LeetCode讨论区.

2.1 暴力法

首先,要想找到第 i i i 位置最大面积(以第 i i i 根柱子为最矮柱子所能延伸的最大面积)是什么?

是以 i i i 为中心,向左找第一个小于 h e i g h t s [ i ] heights[i] heights[i] 的位置 l e f t i left_i lefti;向右找第一个小于于 h e i g h t s [ i ] heights[i] heights[i] 的位置 r i g h t i right_i righti,即最大面积为 h e i g h t s [ i ] ∗ ( r i g h t i − l e f t i − 1 ) heights[i] * (right_i - left_i -1) heights[i](rightilefti1),如下图所示:在这里插入图片描述
所以,我们的问题就变成如何找 r i g h t i right_i righti l e f t i left_i lefti
最简单的思路就是,就是暴力法,直接分别在 i i i 左右移动。

class Solution:
    def largestRectangleArea(self, heights: List[int]) -> int:
        n = len(heights)
        res = 0
        for i in range(n):
            l = i
            r = i
            while l >= 0 and heights[l] >= heights[i]:
                l = l - 1
            while r < n and heights[r] >= heights[i]:
                r = r + 1
            res = max(res, heights[i] * (r - l - 1))
        return res

但是,这是一个时间复杂度为 O ( n 2 ) O(n^2) O(n2),超时。
接下来想办法优化。

2.2 单调栈(空间换时间)

具体可以参考 LeetCode官方题解(视频动画).

所谓单调栈,本质上还是栈,即满足先进后出的原则,在Python 中实现可以用 List 实现。所谓单调,即单调递增或者单调递减。

  1. 单调递增栈可以找到左边第一个比当前出栈元素小(含等于)的元素
  2. 单调递减栈可以找到左边第一个比当前出栈元素大(含等于)的元素

具体解题步骤

  1. 本题中,当看到柱子递增时,将下标入栈,(栈中只存下标,因为高度可以直接通过下标访问);
  2. 当柱子下降时,将栈顶元素 stack[-1] pop 出来,并且计算当前的面积 h e i g h t [ i ] ∗ ( i − s t a c k [ − 1 ] − 1 ) height[i] * (i - stack[-1] - 1) height[i](istack[1]1),注意这里要用弹出一个栈顶后的新栈顶 stack[-1],具体可看下面代码

技巧:这里在最前面和最后面分别加了一个“哨兵”,目的是避免了特殊处理。

class Solution:
    def largestRectangleArea(self, heights: List[int]) -> int:
        heights = [0] + heights + [0]
        n = len(heights)
        stack = []   # 只存下标,因为高度可以直接通过下标访问
        res = 0
        for i in range(n):
            while stack and heights[stack[-1]] > heights[i]:   # 判断栈顶元素是否比较大,即开始下降,需要出栈了
                top = stack.pop()    # 先出栈,栈顶元素就是前面低一点的那个
                res = max(res, heights[top] * (i - stack[-1] - 1)) # 这里不能用 i-top-1 因为top已经被弹出了
            stack.append(i)     # 如果柱子不断上升,则入栈
        return res

参考:

  1. LeetCode题解.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值