[LeetCode] 84. Largest Rectangle in Histogram

题: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;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值