题目描述:
给定 n
个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1
。
求在该柱状图中,能够勾勒出来的矩形的最大面积。
以上是柱状图的示例,其中每个柱子的宽度为 1
,给定的高度为 [2,1,5,6,2,3]
。
图中阴影部分为所能勾勒出的最大矩形面积,其面积为 10
个单位。
示例:
1 | 输入: [2,1,5,6,2,3] |
链接:
https://leetcode-cn.com/problems/largest-rectangle-in-histogram
题目分析
通过观察我们可以知道,对于每一个柱子,都有以它为高的矩形存在,我们只需要遍历所有这样的矩形即可得到最大矩形。每一个矩形的宽边界是哪里呢?是这个柱子两边低于它的第一个柱子。获取这个信息并不需要每次都进行遍历,我们只需要使用单调栈的做法。按照从左到右的顺序遍历,我们维护一个升序的柱子序列,则对于栈顶的柱子,它的左边界就是栈中的前一个柱子,而当我们遍历到某个柱子不符合升序序列,也即比栈顶元素小时,它就是栈顶矩形的右边界。这个时候就可以计算以栈顶为高的矩形的大小了。并且计算后可以将其出栈,这是因为它是目前遍历过的柱子中最高的值,不会对其他矩形产生影响。出栈后,如果这个柱子仍然比新的栈顶小,则仍需要继续这个过程,因此我们使用的是 while 循环。结束过程之后,我们要将这个柱子进栈。
对于连续两个同样高度的柱子是怎么处理的呢?以它们为高的矩形应该是一样的,因此我们只需要计算其中一个即可。两个都加入栈中的话,右边那个的左边界会被认为是左边这个,因此计算得到的矩形面积会比实际小,左边那个的计算则不会受到影响。我们的结果是要求最大的矩形面积,因此偏小的那个不会对最终结果造成影响。由此可知,我们可以直接将同样高度的也加入到栈中。
栈中存放的信息是什么?我们需要知道柱子的高度,也即矩形的高;而柱子的宽度是以它的左右边界的下标差来计算的,因此我们也需要知道柱子的下标。因此我们有两种方法,一种是直接存放柱子的下标,每次需要用到柱子高度时再通过柱子下标查询高度;另外一种是直接用一个 pair
将这两个信息存放进去。
这个题目对于边界是怎么处理的?我们可以利用哨兵的技巧。也即在柱子序列两边添加一个高度为 0 的柱子,这样就可以直接处理边界的柱子了。可是数组不能有下标为 -1 的情况,heights[-1]
是非法的,因此我采用的是 pair
的做法,最开始就在栈中直接添加了一个 {-1, 0}
,表示下标为 -1 的柱子高度为 0。这样的好处是栈永远不会为空,并且这个哨兵可以处理其作为左边界的情况。对于右边界的哨兵就更简单了,直接在数组最后添加一个 0 表示一个高度为 0 的柱子即可,这样到最后可以处理掉栈中剩余的所有柱子,只留下两个哨兵。
1 | class Solution { |
时间复杂度:$O(n)$,其中 $n$ 是柱子的数量。我们只对数组进行了一次遍历。
空间复杂度:$O(n)$,其中 $n$ 是柱子的数量。也即栈的开销。