Solution:
Suppose we are at bar i with height h. To form a rectangle with the whole bar i, bar i could form a rectangle with itself, or any adjacent bars with heights >= h. Thus, for each bar i, if bar j is the closest bar to bar i with j < i and height < h, and if bar k is the closets bar to bar i with k > i and height < h, then the area of the rectangle formed by the whole bar i is h * (k-1 - j-1 + 1). We could calculate the area of rectangle formed by each bar i, and find the maximum among them. The maximum would be the maximum area the question asks for.
A naive solution is :
for each bar i :
int left = i - 1;
int right = i + 1;
while(left >= 0 && heights[left] >= heights[i]) left--; ------------step 1 to find the left bound of the rect
while(right < heights.size() && heights[right] >= heights[i]) right++;--------step 2 to find the right bound of the rect
area = (right - 1 - left - 1 + 1) * heights[i];
But step 1 and 2 is O(n).
To speed these two steps up, we could use two arrays to store bar i's left bound and right bound. (See step 1 and step 2 in the code)
Code: The worst case is still O(n^2)
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
if(heights.empty()) return 0;
int max_area = -1;
int cur = 0;
int m = heights.size();
int leftBound[m];//leftBound[i] = x, it means height[x] is the first element less than height[i] when scanning from i-1 to 0.
int rightBound[m]; //rightBound[i] = x. It means height[x] is the first element less than height[i] when scanning from i + 1 to heights.size() - 1.
int left, right;
leftBound[0] = -1;
rightBound[m - 1] = m;
//construct leftBound[]
for(cur = 1; cur < m; cur ++){-----------step1
if(heights[cur-1] < heights[cur]){
leftBound[cur] = cur - 1;
}
else{
left = cur - 1;
while(left >=0 && heights[left] >= heights[cur])
left = leftBound[left];
leftBound[cur] = left;
}
}
//construct rightBound
for(cur = m - 2; cur >= 0; cur --){-----------step2
if(heights[cur + 1] < heights[cur])
rightBound[cur] = cur + 1;
else{
right = cur + 1;
while(right < m && heights[right] >= heights[cur])
right = rightBound[right];
rightBound[cur] = right;
}
}
cur = 0;
while(cur < m){
left = leftBound[cur]+1;
right = rightBound[cur] - 1;
max_area = max( (right - left + 1)*heights[cur], max_area);
cur ++;
}
return max_area;
}
};
Solution 2 : Use a stack.
We use a stack to record the index of the left closest element to current element which is no smaller than current element.
Given we now scan heights[i].
If height[i] > heights[stack.top()], then we push i into the stack, and proceed to the next i.
if height[i] <= heights[stack.top()], then we pop out stack.top() to calculate the area formed by height[stack.top()].
Code: O(n)
int largestRectangleArea(vector<int>& heights) {
heights.push_back(0);
stack<int> left_ind;
int max_area = 0;
int i = 0;
while(i < heights.size()){
if(left_ind.empty()) {
left_ind.push(i);
i++;
}
else{
if(heights[i] >= heights[left_ind.top()]){
left_ind.push(i);
i++;
}else{
int left = left_ind.top();
left_ind.pop();
max_area = max(max_area, heights[left] *(left_ind.empty()? i :(i - left_ind.top() - 1)));
}
}
}
return max_area;
}