问题描述
给定一个数组 heights,其中每个元素表示柱状图中一个柱子的高度,求由这些柱子组成的最大矩形的面积。矩形必须由相邻的柱子组成。
例如:
输入 heights = [2, 1, 5, 6, 2, 3],输出为 10,因为柱子高度为 [5, 6] 的区域形成最大矩形,宽度为 2,高度为 5。
解法详解:单调栈
单调栈是解决这个问题的最佳方法,可以在 O(n) 时间复杂度内完成。
单调增栈
- 进栈前弹出的都是左边比自己大的→确定左边界;
- 出栈时必定是右边第一次遇到比自己小的→确定右边界、
解法步骤
-
准备工作:
- 使用一个栈存储柱子的索引。
- 在遍历过程中,维护栈中索引对应的柱子高度递增。
-
遍历数组:
- 如果当前柱子的高度大于栈顶柱子的高度,则将当前柱子的索引压入栈。
- 如果当前柱子的高度小于栈顶柱子的高度,则反复弹出栈顶元素,计算以栈顶柱子高度为矩形高度的面积,并更新最大面积。
-
处理剩余柱子:
- 遍历完成后,栈中可能还存有一些柱子的索引,这些柱子以自身为高度形成的矩形面积也需要计算。
-
哨兵技巧:
- 为了简化边界处理,在数组末尾添加一个高度为
0的柱子,确保可以清空栈。
- 为了简化边界处理,在数组末尾添加一个高度为
- 对数组中的每个元素,若假定以它为高,能够展开的宽度越宽,那么以它为高的矩形面积就越大。
- 因此,思路就是找到每个元素左边第一个比它矮的矩形和右边第一个比它矮的矩形,在这中间的就是最大宽度
- 最后对每个元素遍历一遍找到最大值即可。
代码实现
以下是 Python 的代码实现:
def largestRectangleArea(heights):
stack = [] # 栈中存储柱子的索引
max_area = 0
heights.append(0) # 添加一个哨兵,确保栈清空
for i, h in enumerate(heights):
# 当当前柱子高度小于栈顶柱子高度时,计算面积
while stack and heights[stack[-1]] > h:
height = heights[stack.pop()] # 栈顶柱子的高度
width = i if not stack else i - stack[-1] - 1 # 宽度
max_area = max(max_area, height * width) # 更新最大面积
stack.append(i) # 当前柱子索引入栈
return max_area
运行示例
# 示例输入
heights = [2, 1, 5, 6, 2, 3]
# 计算结果
print(largestRectangleArea(heights)) # 输出:10
时间复杂度
-
每个柱子最多入栈一次,出栈一次。
-
因此,时间复杂度为 O(n)。
空间复杂度
- 栈的空间复杂度为 O(n)。
关键要点
-
栈存储的不是高度,而是索引:方便计算矩形宽度。
-
利用哨兵柱子清空栈:在数组末尾加一个高度为 0 的柱子,简化边界处理。
-
动态计算宽度:
- 宽度为当前索引到栈顶下一个索引之间的距离。
- 如果栈为空,宽度等于当前索引。