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].
给出一个数组,判断其构成的直方图的最大矩形面积
思路:
因为要求是矩形面积,短边会限制面积,为了更好地判断短边,所以最好是升序排列,
但是不能排序,因为排序会影响整个形状。
所以采用区间升序,升序区间结束的时候计算升序部分的最大面积
升序部分区间的面积计算可以从左到右,也可以从右到左
比如[1,2,3], 长度是3
从左到右:1* 3 = 3, 2* 2 = 4, 3* 1 = 3 最大面积是4
从右到左:3* 1 = 3, 2* 2 = 4, 1* 3 = 3 最大面积是4
因为是区间升序,比如[1,2,3,1], 升序区间是[1,2,3],
从右到左计算完的元素移出,直到剩下的元素对右边的1不再满足升序条件,这里计算完3,2后移出,直到剩下[1,1],为什么移出,因为短边的限制,所以以后用不到比左右都大的2,3了
因为每次都要和升序区间最右边的元素比较,所以用stack
用stack来保存index,可以通过index取得高度和宽度
stack为空或者新元素>stack.peek()的时候(放入stack后满足升序条件),将新元素push进stack
index到数组最右边超出边界的时候,假定index指向的高度是0,处理剩下所有stack中的元素
举个例子:
input = [1,2,3,1] index=3,stack元素:0,1,2 (保存的是index)
stack最上面:input[2] = 3 > 1 所以pop 出2
计算input[2]* (index - 3左边的index:stack.peek - 1) = 3* (3 - 1 - 1) = 3
以此类推直到剩下1,不再满足升序,所以push(1 (index=3的元素) )
然后index移到input右边界外,index=4,这时处理剩下的[1,1]
注意剩下最后一个1时,1左边已经没有元素,假设左边的index是-1,
那么宽度=index- (-1) -1 = index
public int largestRectangleArea(int[] heights) {
if (heights == null || heights.length == 0) {
return 0;
}
int top = 0;
int area = 0;
int MaxArea = 0;
Stack<Integer> stack = new Stack<>();
for (int i = 0; i < heights.length; i++) {
if (stack.isEmpty() || heights[i] >= heights[stack.peek()]) {
stack.push(i);
} else {
while (!stack.isEmpty() && heights[i] < heights[stack.peek()]) {
top = stack.pop();
if (stack.isEmpty()) {
area = heights[top] * i;
} else {
area = heights[top] * (i - stack.peek() - 1);
}
if (area > MaxArea) {
MaxArea = area;
}
}
stack.push(i);
}
}
while (!stack.isEmpty()) {
top = stack.pop();
if (stack.isEmpty()) {
area = heights[top] * heights.length;
} else {
area = heights[top] * (heights.length - stack.peek() - 1);
}
if (area > MaxArea) {
MaxArea = area;
}
}
return MaxArea;
}
简化代码如下
需要注意的地方是取出cur后,用heights[cur] * 矩形宽时,矩形宽是当前的i 减去上一个保存的index处再减1,而不是简单的i - cur
为什么会这样,举个例子
[1, 2, 3, 1]
stack中保存了下标0,1,2,遇到下标为3的1时处理掉下标1,2,再保存3,那么最后stack中仅有0,3,这时i = 4,计算下标3的面积时肯定应该宽为4-0-1=3,因为还应该包括之间值2,3所在的区域。
需要注意几点,写在代码注释中
public int largestRectangleArea(int[] heights) {
if(heights == null || heights.length == 0) {
return 0;
}
Stack<Integer> stack = new Stack<>();
int result = 0;
for(int i = 0; i < heights.length; i++) {
while(!stack.isEmpty() && heights[i] < heights[stack.peek()]) {
//必须先pop,因为不pop就无法查到前面的index
int cur = stack.pop();
//pop之后stack可能为空,所以要再验证stack是否为空
result = Math.max(result, heights[cur] * (stack.isEmpty()?i:(i-stack.peek()-1)));
}
stack.push(i);
}
//index超过数组右边界时仍然需要处理stack中剩下的
while(!stack.isEmpty()) {
int cur = stack.pop();
result = Math.max(result, heights[cur] * (stack.isEmpty()?heights.length:(heights.length-stack.peek()-1)));
}
return result;
}