如图的直方图的高度为[2, 1, 5, 6, 2, 3] ,每个bar的宽度为1, 能够形成的最大面积的矩形为 2 * 5 = 10。
在这里我会介绍2种解法,一种是很直观的时间复杂度为O(n^2)的解法,另一种是很巧妙地利用了栈的时间复杂度O(n)的解法。
对于每个长条(bar),我们将它看成是矩形中最小长度的长条。对于每个长条,我们都能计算出该长条下,最大的面积。
所以,最终我们能够找到最大的面积。
比如以第一个长条2为例,以它进行周围扩展的话,比它小的左边的是没有了;比它小的右边也没有(它的右边是1),
所以,这个长条能形成的最大面积是2.
再以5为例子,比它小的左边没有,比它小的右边是6,所以它能形成的最大的面积是 2 * 5 。
解法1(复杂度O(n^2))
按照上述思路的基础解法。我们的目的就是寻找到比当前长条小的左smallerIndex, 和右 smallerIndex。
代码:
public int largestRectangleArea(int[] heights) {
int maxArea = 0;
for (int i = 0; i < heights.length; i++) {
maxArea = Math.max(maxArea, calRecArea(heights, i));
}
return maxArea;
}
private int calRecArea(int[] heights, int index) {
int left = index - 1, right = index + 1;
while (left >= 0 && heights[left] >= heights[index])
left--;
while (right < heights.length && heights[right] >= heights[index])
right++;
return (right - left - 1) * heights[index];
}
解法2:
如何能更方便的知道比当前bar更小的左,右bar的index 呢?
我们可以从左到右遍历这个height数组,同时维护一个stack。
每一个bar都会入栈一次, 当碰到更小的height的时候,pop(),
当一个ba pop()了,我们将弹出的bar作为高度最小的bar.
那么此时,这个bar的leftSmalIerIndex 和 rightSmallerIndex 是什么呢?
当前遍历到的index 正是 rightSmallerIndex, 栈顶正是leftSmallerIndex。
代码:
private int largestRectangleAreaStack(int[] heights) {
int i = 0;
int maxArea = 0;
int curArea = 0;
Stack<Integer> store = new Stack<>(); // to store the left smaller index
while (i < heights.length) {
if (store.empty() || heights[store.peek()] <= heights[i]) {
store.add(i);
i++;
} else {
int curTop = store.pop();
// the ‘left smaller index’ is previous (previous to tp) item in stack and ‘right smaller index’ is ‘i’ (current index).
if (!store.isEmpty()) {
curArea = heights[curTop] * (i - store.peek() - 1);
} else {
curArea = heights[curTop] * i;
}
maxArea = Math.max(maxArea, curArea);
}
}
while (!store.isEmpty()) {
int curTop = store.pop();
if (!store.isEmpty()) {
curArea = heights[curTop] * (i - store.peek() - 1);
} else {
curArea = heights[curTop] * i;
}
maxArea = Math.max(maxArea, curArea);
}
return maxArea;
}
参考:
http://www.geeksforgeeks.org/largest-rectangle-under-histogram/