题:https://leetcode.com/problems/largest-rectangle-in-histogram/description/
#题目
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.
Example:
Input: [2,1,5,6,2,3]
Output: 10
#思路
想法:
1.每一个可能成为最大的矩形,它的高度必定是数组中的某个数值。
2.现在的问题是如何求位置i 它对应的长度,两边都要小于heights[i]
3.对于每个i 左右扫描会造成 O(n^2),会报错。现在问题是如何降低时间复杂度。
4.这里引入一个减少时间复杂度的新思想。i的左右边界,可以看它最相邻i-1的左边界和i+1的右边界。边界还可以一直传递下去,只要边界的高度大于i的高度。
引用 Discuss中大神的解释,讲的十分清楚
https://leetcode.com/problems/largest-rectangle-in-histogram/discuss/28902/5ms-O(n)-Java-solution-explained-(beats-96)
For any bar i the maximum rectangle is of width r - l - 1 where r - is the last coordinate of the bar to the right with height h[r] >= h[i] and l - is the last coordinate of the bar to the left which height h[l] >= h[i]
So if for any i coordinate we know his utmost higher (or of the same height) neighbors to the right and to the left, we can easily find the largest rectangle:
int maxArea = 0;
for (int i = 0; i < height.length; i++) {
maxArea = Math.max(maxArea, height[i] * (lessFromRight[i] - lessFromLeft[i] - 1));
}
The main trick is how to effectively calculate lessFromRight and lessFromLeft arrays. The trivial solution is to use O(n^2) solution and for each i element first find his left/right heighbour in the second inner loop just iterating back or forward:
for (int i = 1; i < height.length; i++) {
int p = i - 1;
while (p >= 0 && height[p] >= height[i]) {
p--;
}
lessFromLeft[i] = p;
}
The only line change shifts this algorithm from O(n^2) to O(n) complexity: we don’t need to rescan each item to the left - we can reuse results of previous calculations and “jump” through indices in quick manner:
while (p >= 0 && height[p] >= height[i]) {
p = lessFromLeft[p];
}
#CODE
from collections import defaultdict
class Solution:
def largestRectangleArea(self, heights):
"""
:type heights: List[int]
:rtype: int
"""
if len(heights) == 0:
return 0
lessFromLeft = {}
lessFromRight = {}
lessFromLeft[0] = -1
lessFromRight[len(heights) - 1] = len(heights)
for i in range(1,len(heights)):
p = i - 1
while p>=0 and heights[p]>=heights[i]:
p = lessFromLeft[p]
lessFromLeft[i] = p
for i in range(len(heights)-2,-1,-1):
p = i + 1
while p<=len(heights)-1 and heights[p]>= heights[i]:
p = lessFromRight[p]
lessFromRight[i] = p
MaxArea = 0
for i in range(0,len(heights)):
MaxArea = max(MaxArea,heights[i]*(lessFromRight[i]-lessFromLeft[i]-1))
return MaxArea
超时版本 没有扫描优化
class Solution:
def largestRectangleArea(self, heights):
"""
:type heights: List[int]
:rtype: int
"""
maxsqr = 0
for i in range(len(heights)):
li = i
lr = i + 1
while li-1 >= 0 and heights[li-1] >= heights[i]:
li -= 1
while lr < len(heights) and heights[lr] >= heights[i]:
lr += 1
tmpsqr = (lr - li)*heights[i]
if tmpsqr > maxsqr:
maxsqr = tmpsqr
return maxsqr
二刷
动态规划
一刷写的 思路说的很清楚,降低时间复杂度的方法 就是 使用DP 提升 查找 左右边界的 效率。
状态初始化应该记住下:
left[0] = -1;
right[COL-1] = COL;
是开区间
class Solution {
public int maximalRectangle(char[][] matrix) {
int ROW = matrix.length;
if(ROW == 0)
return 0;
int COL = matrix[0].length;
int[] height = new int[COL];
int[] leftBound = new int[COL];
int[] rightBound = new int[COL];
Arrays.fill(rightBound,COL);
int res = 0;
for(int i =0 ; i < ROW ; i++){
int curLeftBound = 0;
int curRightBound = COL;
for(int j = COL-1;j>=0 ; j--){
if(matrix[i][j]=='1'){
rightBound[j] = Math.min(rightBound[j],curRightBound);
}else{
curRightBound = j;
rightBound[j]=COL;
}
}
for(int j = 0 ; j <COL ; j++){
if(matrix[i][j]=='1'){
height[j]++;
leftBound[j] = Math.max(leftBound[j],curLeftBound);
res = Math.max(res,(rightBound[j] - leftBound[j])*height[j]);
}else{
height[j] = 0;
curLeftBound = j + 1;
leftBound[j] =0;
}
}
}
return res;
}
}