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 heights = [2,1,5,6,2,3]
,
return 10
.
【题目分析】
题目很容易理解,给定一个非负数组代表的直方图,求出图中包含的最大的矩形的面积。
【思路】
1.这题的一个基本思想是以每一个bar为最低点,向左右遍历直到遇到比他小的bar或边界。这样就能找到一个此bar为最低点的矩形面积。遍历所有的bar之后即可找到最大的矩形面积。但是向左右遍历寻找比他小的bar的时间复杂度是O(n),在加上遍历一遍所有的bar,总的时间复杂度将为O(n*n),是无法通过所有数据的。我们在水平方向任意画一条线,如果有条和这条线相交,我们找出相交的矩阵中最大的那个。示例如下:
当线的高度是2时,相交的矩阵有两个,较大的那个面积是8。
当线的高度是3时,相交的矩阵有两个,较大的那个面积时6。
当线的高度是5时,相交的矩阵有一个,面积是10。
重复上面的步骤,找到的最大值是10.这个过程很简单,只需要遍历所有可能出现的高度,然后找到所有出现的矩阵中面积最大的那一个。但是这个过程的算法复杂度较高,为O(N2).
java代码:
1 public class Solution { 2 public int largestRectangleArea(int[] heights) { 3 if(heights.length == 0) return 0; 4 if(heights.length == 1) return heights[0]; 5 6 int curlen = 0; 7 int maxS = 0, curS = 0; 8 9 for(int i = 0; i < heights.length; i++){ 10 curlen = heights[i]; 11 curS = 0; 12 for(int j = 0; j < heights.length; j++){ 13 if(heights[j] >= preminlen) curS += preminlen; 14 else{ 15 maxS = Math.max(curS, maxS); 16 curS = 0; 17 } 18 } 19 maxS = Math.max(curS, maxS); 20 } 21 return maxS; 22 } 23 }
2. 我们需要寻找一种时间复杂度更低的寻找一个bar左右边界的算法。在网上流传了一个设计极其巧妙的方法,借助一个stack可以将时间复杂度降为O(n)。
这种算法的思想是维护一个递增的栈,这个栈保存了元素在数组中的位置。 这样在栈中每一个左边的bar都比本身小,所以左边就天然有界了,也就是左边界就是左边的一个bar。遍历一遍height数组,在将height数组入栈的时候,如果当前元素height[i]比栈顶元素小,则我们又找到了栈顶元素的右边界。因此我们在此时就可以计算以栈顶元素为最低bar的矩形面积了,因为左右边界我们都已经找到了,而且是在O(1)的时间复杂度内找到的。然后就可以将栈顶元素出栈了。这样每出栈一个元素,即计算以此元素为最低点的矩形面积。当最终栈空的时候我们就计算出了以所有bar为最低点的矩形面积。为保证让所有元素都出栈,我们在height数组最后加一个0,因为一个元素要出栈必须要遇到一个比他小的元素,也就是右边界。
- 如果已知height数组是升序的,应该怎么做?
比如1,2,5,7,8
那么就是(1*5) vs. (2*4) vs. (5*3) vs. (7*2) vs. (8*1)
也就是max(height[i]*(size-i))
- 使用栈的目的就是构造这样的升序序列,按照以上方法求解。
但是height本身不一定是升序的,应该怎样构建栈?
比如2,1,5,6,2,3
(1)2进栈。s={2}, result = 0
(2)1比2小,不满足升序条件,因此将2弹出,并记录当前结果为2*1=2。
将2替换为1重新进栈。s={1,1}, result = 2
(3)5比1大,满足升序条件,进栈。s={1,1,5},result = 2
(4)6比5大,满足升序条件,进栈。s={1,1,5,6},result = 2
(5)2比6小,不满足升序条件,因此将6弹出,并记录当前结果为6*1=6。s={1,1,5},result = 6
2比5小,不满足升序条件,因此将5弹出,并记录当前结果为5*2=10(因为已经弹出的5,6是升序的)。s={1,1},result = 10
2比1大,将弹出的5,6替换为2重新进栈。s={1,1,2,2,2},result = 10
(6)3比2大,满足升序条件,进栈。s={1,1,2,2,2,3},result = 10
栈构建完成,满足升序条件,因此按照升序处理办法得到上述的max(height[i]*(size-i))=max{3*1, 2*2, 2*3, 2*4, 1*5, 1*6}=8<10
综上所述,result=10
java代码:
1 public class Solution { 2 public int largestRectangleArea(int[] height) { 3 int len = height.length; 4 Stack<Integer> s = new Stack<Integer>(); 5 int maxArea = 0; 6 for(int i = 0; i <= len; i++){ 7 int h = (i == len ? 0 : height[i]); 8 if(s.isEmpty() || h >= height[s.peek()]){ 9 s.push(i); 10 }else{ 11 int tp = s.pop(); 12 maxArea = Math.max(maxArea, height[tp] * (s.isEmpty() ? i : i - 1 - s.peek())); 13 i--; 14 } 15 } 16 return maxArea; 17 } 18 }