题目
题目链接
Given n non-negative integers representing the histogram’s bar height where the width of each bar is 1, find the area of largest rectangle in the histogram.
Above is a histogram where width of each bar is 1, given height = [2,1,5,6,2,3].
The largest rectangle is shown in the shaded area, which has area = 10 unit.
Example:
Input: [2,1,5,6,2,3]
Output: 10
分析
暴力搜索
依次取区间arr(i..j)
,用min(arr(i..j)) * j - i
可以遍历所有可能的长方形,从中选取最大的即可.此时时间复杂度为$O(N^2)$.
观察暴力解法发现:
- 最大面积的长方形长度必然和数组中的某个元素的值相等.即在数组中只有
arr.length
个候选最大长方形.此时我们要设法确定每个元素高度的长方形的宽度. - 暴力搜索的过程没有做任何记录,每次搜索对下一次没有任何帮助,可以设法记录有效信息简化搜索过程.即空间换时间.
- 输入数组元素均为非负整数,在数组两侧各添加一个0不改变最终结果(哨兵,Sentinel).如此可省略处理edge case的部分,简化代码.
算法改进
在当前元素严格小于上一个元素时,便决定了此前某些元素的宽度无法再拓展了.此时以当前下标为右界,上一个元素的下标为左界,便可确定以此元素为高的矩形的宽度.
我们可以用栈存下标.就拿例题来说,左侧哨兵下标i=-1
,循环前先入栈.
i = 0,a[i] = 2
还有继续向右拓宽的可能,下标入栈.i = 1,a[i] = 1
此时:- 栈尾元素
a[s.back()]
,即下标为0的元素2大于当前元素.高度为2的长方形无法再拓宽,退栈处理.宽度为当前下标1减去退栈后的栈尾元素下标此时为哨兵-1再减去1,即width = i - s.back() - 1; area = width * height;
. - 再次检查栈尾,发现哨兵高度为0还有拓宽可能,不做处理(这样可能更好理解一些,实际情况是栈内只剩下哨兵,不做处理.再第四步还有一次演示).
- 当前下标元素也有拓宽可能,下标入栈.
- 栈尾元素
i = 2,a[i] = 5;i = 3,a[i] = 6
还有继续向右拓宽的可能,入栈.i = 4,a[i] = 2
与第二步做相似处理,先后确定了以6和以5为高度的长方形的面积.在6,5分别退栈后访问到a[1]
,发现当前下标元素不小于a[1]
.即栈内所有元素均还有拓宽可能,故不作处理.当前下标也入栈.i = 5,a[i] = 3
还有继续向右拓宽的可能,入栈. 再次强调哨兵i = 6,a[i] = 0
元素访问完毕,下标来到哨兵,此时栈内除了另一个哨兵外元素全部大于哨兵.对栈内元素依次退栈计算面积,直到栈内只剩下哨兵为止.
- 绿色:宽度还有可能变长,入栈. 红色:宽度无法拓展,出栈计算面积. 虚线:以此元素为高度的长方形面积已确定. (要想象首尾各有一个0)
代码
1 | class Solution { |
后续
- 在有连续个值相同的元素时,中间会有多次错误计算,但并不影响连续元素中最后一次退栈的计算成为最大值.
- JAVA-为什么用Deque去实现Stack?