http://blog.csdn.net/abcbc/article/details/8943485
http://fisherlei.blogspot.com/2012/12/leetcode-largest-rectangle-in-histogram.html
循环有不同的写法
它这种i先动,j从i往左扫(往回扫)的方式,很适合第二种方法的剪枝,是从范围小到范围大,正好当后面比前面大的话,当前的i就没必要往前扫了,后面肯定比当前的i组成的面积大
一般的i,j都往后扫,不好剪枝啊,是范围大道范围小了
O(n)的解法,
http://www.geeksforgeeks.org/largest-rectangle-under-histogram/
We have discussed a Divide and Conquer based O(nLogn) solution for this problem. In this post, O(n) time solution is discussed. Like the previous post, width of all bars is assumed to be 1 for simplicity.For every bar ‘x’, we calculate the area with ‘x’ as the smallest bar in the rectangle. If we calculate such area for every bar ‘x’ and find the maximum of all areas, our task is done. How to calculate area with ‘x’ as smallest bar? We need to know index of the first smaller (smaller than ‘x’) bar on left of ‘x’ and index of first smaller bar on right of ‘x’. Let us call these indexes as ‘left index’ and ‘right index’ respectively.
We traverse all bars from left to right, maintain a stack of bars. Every bar is pushed to stack once. A bar is popped from stack when a bar of smaller height is seen. When a bar is popped, we calculate the area with the popped bar as smallest bar. How do we get left and right indexes of the popped bar – the current index tells us the ‘right index’ and index of previous item in stack is the ‘left index’. Following is the complete algorithm.
就是说,还是跟另外一个矩形题(Trapping Rain Water)类似的思路,对于每个i位置,如果都能求出以它为最小高度的时候,所能扩展的最大矩形,那么求出所有的就可以解决问题了。
那么对于每一个i位置,当向右遍历的时候,很容易知道右侧比他小的位置(因为正在往右遍历。。),那么如何快速知道左侧比它小的位置呢,那么就是用所谓的单点栈,维护一个不减栈,或者叫不减队列,里面存下标,那么每次从里面取出来小于当前i的height就是左侧位置了。
用stack保存每个i位置的最左侧小于height[i]的下标,也就是维护一个单调栈(单调不减)。
注意栈内保存的是下标,不是高度
所以栈里面就是一直pop出来小于的,那就是最左侧小于height[i]的小标了
class Solution {
public:
int largestRectangleArea(vector<int> &height) {
int sum = 0;
int n = height.size();
stack<int> index_stack;
int i = 0;
while (i < n) {
if (index_stack.empty() || height[index_stack.top()] <= height[i])
index_stack.push(i++);
else {
int tp = index_stack.top();
index_stack.pop();
// 这里面geeksforgeeks的感觉不太好,没有这个循环,那么跟求解的本意是不太一样的
// 比如215523, 当55的时候,此时后面取到2,没有循环的话对于下标为3的5,求出的左边界实际上不准确的,求出面积是5.。。
// 所以循环一下更符合求解思想
while (!index_stack.empty() && height[tp] == height[index_stack.top()]) {
tp = index_stack.top();
index_stack.pop();
}
sum = max(sum, height[tp] * (index_stack.empty() ? i : i - index_stack.top() - 1));
}
}
while (!index_stack.empty()) {
int tp = index_stack.top();
index_stack.pop();
while (!index_stack.empty() && height[tp] == height[index_stack.top()]) {
tp = index_stack.top();
index_stack.pop();
}
sum = max(sum, height[tp] * (index_stack.empty() ? i : i - index_stack.top() - 1));
}
return sum;
}
};