zhangdizhangdi

84. 柱状图中最大的矩形 🟥

题目

leetcode 困难

题目

给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。

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

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

题解

解题思路

我们需要维护一个高度单调递增的栈(栈里存的是柱子的索引):

  • 走上坡路(入栈):如果当前柱子比栈顶的柱子高,说明栈顶的柱子还能继续向右延伸,咱们先别急着算它的面积,把它(的索引)乖乖压入栈中。
  • 走下坡路(出栈并计算):如果遇到一个柱子比栈顶的矮,好戏上演了!
    • 这说明栈顶柱子的“右边界”找到了!(就是当前这个矮柱子)。
    • 那它的“左边界”是谁呢?因为栈是单调递增的,所以栈顶下面压着的那个元素,就是它左边第一个比它矮的柱子!
    • 左右边界都有了,立刻把栈顶弹出,计算它的面积。
    • 一直弹出,直到当前柱子不再比栈顶矮,再把它入栈。
复杂度
  • 时间复杂度:O(N)
  • 空间复杂度:O(N)
js
function largestRectangleArea(heights) {
  let maxArea = 0
  // 技巧:在数组首尾各加一个 0 作为“哨兵”
  // 首部 0:防止栈空,提供绝对的左边界
  // 尾部 0:强行让所有仍在栈里的元素出栈并结算
  const newHeights = [0, ...heights, 0]

  // 栈里存放的是索引
  const stack = []

  for (let i = 0; i < newHeights.length; i++) {
    console.log('♻️ i', i, '【', newHeights[i], '】')
    console.log(
      'stack',
      stack.map(idx => `${idx}${newHeights[idx]}】`).toString(),
    )

    // 如果遇到了“矮子”(当前高度 < 栈顶高度),说明栈顶柱子的右边界确定了
    while (
      stack.length > 0 &&
      newHeights[i] < newHeights[stack[stack.length - 1]]
    ) {
      // 1. 被结算的柱子出栈,它是我们要计算的高
      const hIdx = stack.pop()
      const h = newHeights[hIdx]

      console.log('出栈柱子', hIdx, '【', h, '】')

      // 2. 此时的新栈顶,就是被弹出柱子的“左边界”
      const leftIdx = stack[stack.length - 1]

      // 3. 当前柱子 i,就是被弹出柱子的“右边界”
      // 宽度 = 右边界 - 左边界 - 1
      const w = i - leftIdx - 1

      console.log('左边界', leftIdx, '【', newHeights[leftIdx], '】')

      // 4. 计算面积并挑战最大值
      maxArea = Math.max(maxArea, h * w)
    }

    console.log('maxArea', maxArea)

    // 走上坡路或平路,直接入栈等待未来的右边界
    stack.push(i)

    console.log(
      'stack',
      stack.map(idx => `${idx}${newHeights[idx]}】`).toString(),
    )
  }

  return maxArea
}

console.log('🌰', largestRectangleArea([2, 1, 5, 6, 2, 3]))
console.log('----------------')
console.log('🌰', largestRectangleArea([2, 4]))
执行结果
♻️ i 0 【 0 】
stack 
maxArea 0
stack 0【0】
♻️ i 1 【 2 】
stack 0【0】
maxArea 0
stack 0【0】,1【2】
♻️ i 2 【 1 】
stack 0【0】,1【2】
出栈柱子 1 【 2 】
左边界 0 【 0 】
maxArea 2
stack 0【0】,2【1】
♻️ i 3 【 5 】
stack 0【0】,2【1】
maxArea 2
stack 0【0】,2【1】,3【5】
♻️ i 4 【 6 】
stack 0【0】,2【1】,3【5】
maxArea 2
stack 0【0】,2【1】,3【5】,4【6】
♻️ i 5 【 2 】
stack 0【0】,2【1】,3【5】,4【6】
出栈柱子 4 【 6 】
左边界 3 【 5 】
出栈柱子 3 【 5 】
左边界 2 【 1 】
maxArea 10
stack 0【0】,2【1】,5【2】
♻️ i 6 【 3 】
stack 0【0】,2【1】,5【2】
maxArea 10
stack 0【0】,2【1】,5【2】,6【3】
♻️ i 7 【 0 】
stack 0【0】,2【1】,5【2】,6【3】
出栈柱子 6 【 3 】
左边界 5 【 2 】
出栈柱子 5 【 2 】
左边界 2 【 1 】
出栈柱子 2 【 1 】
左边界 0 【 0 】
maxArea 10
stack 0【0】,7【0】
🌰 10
----------------
♻️ i 0 【 0 】
stack 
maxArea 0
stack 0【0】
♻️ i 1 【 2 】
stack 0【0】
maxArea 0
stack 0【0】,1【2】
♻️ i 2 【 4 】
stack 0【0】,1【2】
maxArea 0
stack 0【0】,1【2】,2【4】
♻️ i 3 【 0 】
stack 0【0】,1【2】,2【4】
出栈柱子 2 【 4 】
左边界 1 【 2 】
出栈柱子 1 【 2 】
左边界 0 【 0 】
maxArea 4
stack 0【0】,3【0】
🌰 4