84. Largest Rectangle in Histogram
1. 问题描述:
输入是一个int数组,代表如图的直方图。要求输出是直方图能够覆盖的最大面积。
2. 解题思路:
直白的看这道题,最大的面积一定是从某个柱出发,向左向右延展,然后得到最大的面积。于是问题来了,从哪个柱开始延展,延展到哪个柱就能得到最大的面积。
首先,如题目图中示例,我们先把目光聚焦到5,6,2这三根柱,忽略掉其他的柱。图中已经画出5,6覆盖了最大面积,想象如果继续向右延伸到2,那么2这根柱势必拉低了5,6覆盖面积的成果。但如果是5,6,7,那么势必要向右延伸到7,并且5,6,7覆盖的面积一定大于5,6覆盖的面积。这样我们就得出一个结论:如果遍历到下个柱是递增趋势,那么一定要延展到下一个柱。
得到这个结论之后,题目就好解了。为了好理解这里的解决思路,我们使用了两个栈,栈 index_stack 存储递增柱的索引值(之后计算覆盖矩形面积的长),栈 height_stack 存储递增柱的值(之后计算覆盖矩形面积的高)。所以我们只去计算栈中柱所覆盖的最大面积。面积计算的公式就是: S=(i−(left−1)−1)∗height_stack.pop() 。这里 (i−(left−1)−1) 就是覆盖矩形面积的长, i 是现在遍历到的柱的索引值, left−1 是 index_stack 的栈顶元素减一。 height_stack.pop() 就是存柱值的栈顶元素。
拿题目图中示例演示其中一个阶段。当1,5,6在栈中,此时指针已经遍历到2。
- 那么2因为递减趋势,不加入栈中,开始计算面积。
(i−(left−1)−1)
中的
i
就是2所在的索引值,
left−1 是5所在的索引值。 height_stack.pop() 就是6。这时计算的就是6这个柱的面积,把这个面积和当前最大面积比较,比当前面积大就替换。然后弹出6柱和其索引。 - 比较指针所在的2柱和此时栈顶元素5,发现仍是递减,那继续计算面积,就是 (2柱索引值−1柱索引值−1)∗5 ,就是5向右延伸到6的面积。比较面积替换,然后弹出5柱和其索引。
- 比较指针所在的2柱和此时栈顶元素1,发现此时是递增,那么把2柱加入
height_stack
,同时把
left
加入
index_stack
中。这里要注意,为什么不加入2柱所在的索引,而要加入
left
。原因很简单,这里
left
是 5 柱所在的索引值,可以当成 5,6 柱现在柱高都是2。当指针扫到最后为空时,计算2柱覆盖面积时,覆盖矩阵的长度要包括5和6。
大致思路就是这样,代码具体的还要注意边界的处理。这里是用了两个栈,其实也可以只用一个栈,减少算法的空间复杂度。
3. java代码
public class Solution {
public int largestRectangleArea(int[] height) {
if(height.length == 0)
return 0;
int largest_area = 0;
Stack<Integer> height_stack = new Stack<Integer>();//存放递增的柱
Stack<Integer> index_stack = new Stack<Integer>();//存放递增柱的索引
for(int i=0;i<height.length;i++){
if(height_stack.empty() || height_stack.peek()<=height[i]){ //增加递增柱
index_stack.push(i);
height_stack.push(height[i]);
} else { //error
int left = 0; //左边界
while(!height_stack.empty() && height_stack.peek()>height[i]){//依次弹出栈顶元素,直到栈顶元素小于当下索引的元素
left = index_stack.pop();
int cur_area = (i - (left-1) -1) * height_stack.pop(); //跨度 i - j - 1
if(cur_area>largest_area) {
largest_area = cur_area;
}
}
height_stack.push(height[i]);
index_stack.push(left);
}
}
while(!height_stack.empty()){
int cur_area = (height.length - (index_stack.pop()-1)-1) * height_stack.pop();
if(cur_area>largest_area){
largest_area = cur_area;
}
}
return largest_area;
}
}
4. 算法评估
希望大家多多指正交流。