Problem:
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.
For example,
Given height = [2,1,5,6,2,3]
,
return 10
.
Analysis:
This problem is very very tirkcy, but elegnat! The idea behind the solution is super powerful, you should clearly think about it.
Possible solution:
For each element in the height array.
1. go right until nums[left] < cur_height
2. then go left until nums[right] < cur_height
Then caculated the area of rectanlge of the encolsed region: cur_height * [(right-1) - (left-1) - 1]
They key point for each element is to find out the left broader and right broader.
Apparently, this method would take O(n^2). Can we do it more smartly?
iff height[cur-1]'s boundary is clear, if we still compare all elements before height[cur-1], which were already compared by height[cur-1]. There must be way to avoid those uncessary comparison. Algorithm:
Take advantage of a stack, the stack's elments are always in the "small-to-large" order.
Step 1: scan the height array from left to right.
(While) Once the current element <= top element at the stack. we pop out an element.
for (int i = 0; i < height.length; i++) {
while (!stack.isEmpty() && height[i] <= height[stack.peek()]) {
...
}
Note: this can guarantee all elements in the stack array in the ascending order. For each poped element, we can get its left border and right border, through following way.
1. the stack still have elements.
(not included)left border: the current top element in the stack, since it is the first element smaller than the poped element.
(not included)right border: the current element height[i]. Since only when "height[i] <= height[stack.peek()]", the poped element appears. The rectangle's area: (right border-1) - (left border+1) + 1 = (i - 1) - (stack.peek()+1) + 1 = (i - stack.peek() + 1) * cur_height. 2. the stack does not have elements.
(included)left border: It means all elements appear before the poped element are actually larger than the poped element, we could start from the first element of the array.
(not included)right border: The current element height[i]. Since only when "height[i] <= height[stack.peek()]", the poped element appears. The rectangle's area : (right border-1) - (left border) + 1 = (i - 1) - (0) + 1 = i * cur_height Implementation:
-------------------------------------------------------------------------------------------------
for (int i = 0; i < height.length; i++) {
while (!stack.isEmpty() && height[i] <= height[stack.peek()]) {
int cur_height = height[stack.pop()];
cur_max = stack.isEmpty() ? i*cur_height : (i-stack.peek()-1)*cur_height;
max = Math.max(max, cur_max);
}
stack.push(i);
}
------------------------------------------------------------------------------------------------- One idea should be kept in mind:
When we push a element into the stack, we pop out all elements larger than it.
Thus when we need to get left broder, we can directly get from stack.peek() after the poped operation. Since for all elements appeared after the pushed element, must take the pushed element as left border. Those poped elements would not affect them. <What a great idea!> Note: In this problem, we actually set one 0 at left side and one 0 at right side of the height array.
0 [height] 0.
For the above question, we must take care the case when all height elements were scaned, but there are still elements in the stack, thus we must use the right fake border. nums[height] = 0.
Solution:
public class Solution {
public int largestRectangleArea(int[] height) {
if (height == null || height.length == 0)
return 0;
Stack<Integer> stack = new Stack<Integer> ();
int max = 0, cur_max = 0;
for (int i = 0; i < height.length; i++) {
while (!stack.isEmpty() && height[i] <= height[stack.peek()]) {
int cur_height = height[stack.pop()];
cur_max = stack.isEmpty() ? i*cur_height : (i-stack.peek()-1)*cur_height;
max = Math.max(max, cur_max);
}
stack.push(i);
}
while (!stack.isEmpty()) {
int cur_height = height[stack.pop()];
cur_max = stack.isEmpty() ? height.length*cur_height : (height.length-stack.peek()-1)*cur_height;
max = Math.max(max, cur_max);
}
return max;
}
}