leetcode84 –柱状图中最大的矩形

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

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

示例 1:

输入:heights = [2,1,5,6,2,3]
输出:10
解释:最大的矩形为图中红色区域,面积为 10

示例 2:

输入: heights = [2,4]
输出: 4

暴力求解:会超时

# [84] 柱状图中最大的矩形
#

# @lc code=start
class Solution:
    def largestRectangleArea(self, heights: List[int]) -> int:
        maxs=0
        for i in range(len(heights)):
            for j in range(i,len(heights)):
                ms=min(heights[i:j+1])
                maxs=max(maxs,ms*(j-i+1))
        return maxs
# @lc code=end

使用栈求解:

  • 首先我们枚举某一根柱子 i 作为高 h=heights[i];
  • 随后我们需要进行向左右两边扩展,使得扩展到的柱子的高度均不小于 h。换句话说,我们需要找到左右两侧最近的高度小于 h 的柱子,这样这两根柱子之间(不包括其本身)的所有柱子高度均不小于 h,并且就是 i 能够扩展到的最远范围。

我们用一个具体的例子 [6, 7, 5, 2, 4, 5, 9, 3] 来帮助读者理解单调栈。我们需要求出每一根柱子的左侧且最近的小于其高度的柱子。初始时的栈为空。

  • 我们枚举 6,因为栈为空,所以 6 左侧的柱子是「哨兵」,位置为 -1。随后我们将 6 入栈。
    • 栈:[6(0)]。(这里括号内的数字表示柱子在原数组中的位置)
  • 我们枚举 7,由于 6<7,因此不会移除栈顶元素,所以 7 左侧的柱子是 6,位置为 0。随后我们将 7 入栈。
    • 栈:[6(0), 7(1)]
  • 我们枚举 5,由于 7≥5,因此移除栈顶元素 7。同样地,6≥5,再移除栈顶元素 6。此时栈为空,所以 5 左侧的柱子是「哨兵」,位置为 −1。随后我们将 5 入栈。
    • 栈:[5(2)]
  • 接下来的枚举过程也大同小异。我们枚举 2,移除栈顶元素 5,得到 2 左侧的柱子是「哨兵」,位置为 −1。将 2 入栈。
    • 栈:[2(3)]
  • 我们枚举4,5 和 9,都不会移除任何栈顶元素,得到它们左侧的柱子分别是 2,4 和 5,位置分别为 3,4 和 5。将它们入栈。
    • 栈:[2(3), 4(4), 5(5), 9(6)]
  • 我们枚举 3,依次移除栈顶元素 9,5 和 4,得到 3 左侧的柱子是 2,位置为 3。将 3 入栈。
    • 栈:[2(3), 3(7)]

这样以来,我们得到它们左侧的柱子编号分别为 [-1, 0, -1, -1, 3, 4, 5, 3]。用相同的方法,我们从右向左进行遍历,也可以得到它们右侧的柱子编号分别为 [2, 2, 3, 8, 7, 7, 7, 8],这里我们将位置 8 看作「哨兵」。

在得到了左右两侧的柱子之后,我们就可以计算出每根柱子对应的左右边界,并求出答案了。


# @lc code=start
class Solution:
    def largestRectangleArea(self, heights: List[int]) -> int:
        # maxs=0
        # for i in range(len(heights)):
        #     for j in range(i,len(heights)):
        #         ms=min(heights[i:j+1])
        #         maxs=max(maxs,ms*(j-i+1))
        # return maxs
        n = len(heights)
        left, right = [0] * n, [0] * n

        mono_stack = list()
        for i in range(n):
            while mono_stack and heights[mono_stack[-1]] >= heights[i]:
                mono_stack.pop()
            left[i] = mono_stack[-1] if mono_stack else -1
            mono_stack.append(i)
        
        mono_stack = list()
        for i in range(n - 1, -1, -1):
            while mono_stack and heights[mono_stack[-1]] >= heights[i]:
                mono_stack.pop()
            right[i] = mono_stack[-1] if mono_stack else n
            mono_stack.append(i)
        
        ans = max((right[i] - left[i] - 1) * heights[i] for i in range(n)) if n > 0 else 0
        return ans

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注