面试知识库

84. 柱状图中最大的矩形 — 单调栈解法详解#

题目描述#

给定 n 个非负整数,表示柱状图中各柱子的高度,每个柱子宽度为 1。 求柱状图中能勾勒出的矩形的最大面积。

输入: heights = [2,1,5,6,2,3]
输出: 10
plaintext
      █
    █ █
    █ █
    █ █   █ 
█   █ █ █ █ 
█ █ █ █ █ █
2 1 5 6 2 3

最大矩形:高=5,宽=2(第2、3根柱子) → 面积=10
plaintext

暴力思路(先理解本质)#

关键观察:以第 i 根柱子为高度的最大矩形, 宽度 = 向左延伸到第一个比它矮的柱子 + 向右延伸到第一个比它矮的柱子。

对每根柱子 i:
  left[i]  = 左边第一个严格小于 heights[i] 的下标
  right[i] = 右边第一个严格小于 heights[i] 的下标
  面积     = heights[i] × (right[i] - left[i] - 1)
plaintext

暴力做法:对每根柱子向左右各扫一遍 → O(N²) 优化方向:用单调栈一次求出所有 left[] 和 right[]


单调栈核心思想#

维护一个单调递增栈(栈底到栈顶,高度递增)。

当遇到一根比栈顶矮的柱子时:

  • 栈顶柱子找到了右边第一个比它矮的柱子(当前柱子)
  • 弹出栈顶后,新栈顶就是左边第一个比它矮的柱子
  • 此时即可计算栈顶柱子能构成的最大矩形面积
单调递增栈:保证每个元素出栈时,左右边界都已确定
plaintext

哨兵技巧(简化边界处理)#

在 heights 首尾各加一个高度为 0 的哨兵:

原数组: [2, 1, 5, 6, 2, 3]
加哨兵: [0, 2, 1, 5, 6, 2, 3, 0]
         ↑                    ↑
       左哨兵              右哨兵(强制清空栈)
plaintext
  • 左哨兵:保证栈不会空,left 边界统一处理
  • 右哨兵:遍历结束时强制把栈中剩余元素全部弹出计算

完整 Java 代码#


执行过程图解(heights = [2,1,5,6,2,3])#

加哨兵后:h = [0, 2, 1, 5, 6, 2, 3, 0],下标 0~7


宽度计算公式说明#

弹出 mid 时:
  left  = stack.peek()  ← 新栈顶,左边第一个比 h[mid] 小的
  right = i             ← 当前遍历位置,右边第一个比 h[mid] 小的

  width = right - left - 1
          ↑ 不含 left 和 right 两端(它们都比 h[mid] 矮)
plaintext
例:下标  0  1  2  3  4
   高度  0  1  5  1  0
              ↑
           mid=2, h=5
   left=1(h=1), right=3(h=1)
   width = 3-1-1 = 1  ✓
plaintext

单调性维护示意#


复杂度分析#

维度复杂度说明
时间O(N)每个元素最多入栈、出栈各一次
空间O(N)栈的最大深度

易错点汇总#

易错点正确做法
忘记处理遍历结束后栈中的残留元素加右哨兵 h=0 强制清空
栈空时 stack.peek() 报错加左哨兵,栈永远不会空
宽度算成 right - left应为 right - left - 1(不含两端)
条件用 <= 还是 <用严格 <,等高柱子可以合并处理

相关题目#

题号题目关联点
85最大矩形本题逐行应用
42接雨水同样用单调栈,方向相反
239滑动窗口最大值单调队列变体