[leetcode] LargestRectangleHistogram MaximalRectangle

LargestRectangleHistogram

  • 问题描述:给定一个直方图,假设每个bar的width为1,高度不定。计算直方图中所能构成最大矩阵的面积。比如下图中的直方图,所能构成的最大面积就是10.
    -在这里插入图片描述
  • 分析:假设直方图的左边起点是L,右边起点是R。则如果LR全用的话,则所构成的面积一定是 ( R − L ) ∗ H m i n (R-L)*H_{min} (RL)Hmin, H m i n H_{min} Hmin表示是LR中最短的 b a r m i n bar_{min} barmin的高度。而且只要我们使用了 b a r m i n bar_{min} barmin,所构成的面积一定是小于等于 ( R − L ) ∗ H m i n (R-L)*H_{min} (RL)Hmin的。那如果我们不用呢?那我们就可以将所有的Bar划分为 [ L ~ b a r m i n ) [L~bar_{min}) [Lbarmin) ( b a r m i n ~ R ] (bar_{min}~R] (barminR]。同样我们就可以利用递归的思路来处理。
  • 通过上面的分析,我们可以明显的将其转化成一个二分法的问题。
    • Corn:输入nums, L=0, R=N-1;
    • 找到最短Bar的下标Index
    • temp1 = ( R − L + 1 ) ∗ H m i n (R-L+1)*H_{min} (RL+1)Hmin
    • temp2 = Corn(nums, Index+1, R);
    • temp3 = Corn(nums, L, Index-1);
    • return min(temp1, temp2, temp3);
  • 分析时间复杂度:因为我们二分法里面需要找L到R的最小值,所以我们的时间复杂度还是O(N^2).最坏情况下,如果nums是有序的(increase order),则我们不需要进行二分,通过O(N)的遍历就可以: r e s = m a x ( r e s , h e i g h t [ i ] ∗ ( R − i + 1 ) ) res = max(res, height[i]*(R-i+1)) res=max(res,height[i](Ri+1))。同样decrease order也可以得到相同的结果。
  • 代码:
class Solution {
public:
    int largestRectangleAreaBase(const vector<int >& heights, int l, int r){
        if(l > r)
            return 0;
        int min_height = -1;
        int min_height_index = l;
        bool increase_sorted = true;
        bool decrease_sorted = true;
        for(int i=l;i<=r;i++){
            if(min_height == -1 || min_height > heights[i]){
                min_height = heights[i];
                min_height_index = i;
            }
            if(i != r && heights[i] > heights[i + 1]){
                increase_sorted = false;
            }
            if(i != r && heights[i] < heights[i + 1]){
                decrease_sorted = false;
            }
        }
        if(increase_sorted){
            int res = 0;
            for(int i=r;i>=l;i--){
                res = max(res, heights[i] * (r-i+1));
            }
            return res;
        }
        if(decrease_sorted){
            int res = 0;
            for(int i=l;i<=r;i++){
                res = max(res, heights[i] * (i-l+1));
            }
            return res;
        }
        int res = min_height * (r - l + 1);
        res = max(res, max(
                largestRectangleAreaBase(heights, l, min_height_index-1),
                largestRectangleAreaBase(heights, min_height_index + 1, r)));
        return res;
    }
    int largestRectangleArea(vector<int>& heights) {
        int res = 0;
        int l = 0;
        int r = (int) heights.size()-1;
        res = largestRectangleAreaBase(heights, l, r);
        return res;
    }
    static void solution(){
        vector<int> heights = {9, 8, 7, 6, 5, 4, 3, 2, 1};
        Solution solution1;
        cout<<solution1.largestRectangleArea(heights)<<endl;
    }
};

MaximalRectangle

  • 问题描述:输入一个二维矩阵,里面的元素都是0 或者 1。计算二维矩阵内全部由1构成的子矩阵的面积最大值。例如下图所示,red box框中的就是最大的子矩阵。
    在这里插入图片描述
  • 解决思路:
    • 将其可以转化为LargestRectangleHistogram的问题。
      • 假设矩阵一共有M行,N列。
      • 每一行,我们都有一个高度heights, 代表每一位从第1行到该行连续1的个数。通过这样的转化,我们每一行都可以看成一个histogram。
      • 所以我们就是要计算每个histogram的最大矩阵。
      • heights的值一开始初始化成0
      • 如果matrix[i][j] == 1, heights[j] += 1
      • 否则 heights[j] = 0
      • 代码:
    int maximalRectangle(vector<vector<char>>& matrix) {
            int m = (int) matrix.size();
            if(m == 0)
                return 0;
            int n = (int) matrix[0].size();
            vector<int > heights(n);
            for(int i=0;i<n;i++)
                heights[i] = 0;
            int res = 0;
            for(int i=0;i<m;i++){
                for(int j=0;j<n;j++){
                    heights[j] = matrix[i][j] == '0' ? 0: heights[j] + 1;
                }
                res = max(res, largestRectangleArea(heights));
            }
            return res;
        }
    
    • 还有另一种思路就是动态规划,DP
      • 针对每个位置i, j 我们可以该位置的height,left, right值。
        • height的定义和上面的方法一致。
        • 因为光知道height还是不行的,我们要计算width,而width就需要知道左右两点的坐标,这就是left 和 right的职责。
        • 如果该点为0,则left和right是什么都无所谓,因为我们的height是0. 但是因为我们使用一维数组,所以left=0, right=N-1;
        • 如果该点是1,假设其处在一个连续的1111序列中,坐标的坐标是L,右边的左边是R,则left = max(left, L). 这也就说明了为什么我们前面要将left置为0,right置为1。假设说上一行该列没有1,则left就不受上一行的影响。如果上一行该列也有1,并且对应的left大于L的话,则我们应该取较大的那个,这里相当于取交集,因为该点的height也是大于1的。
        • 可能有同学疑问,那如果一行很长的呢?其对应的area就是通过那些上下行都没有1,计算得到的area。
      • 代码:
    int maximalRectangleWithDP(vector<vector<char>>& matrix){
        // target: 计算一个矩阵中,全部由1组成的子矩阵的最大面积
        int m = (int) matrix.size();
        if(m == 0)
            return 0;
        int n = (int) matrix[0].size();
        int left[n];   // 针对每一行,如果该位置是1,则连续是1的,且位于最左边的位置
        int right[n]; // 针对每一行,如果该位置是1,则连续是1的,且位于最右边的位置
        int height[n]; // 针对每一行的,连续是1 的高度
        memset(left, 0, sizeof(left));
        for(int i=0;i<n;i++)
            right[i] = n;
        memset(height, 0, sizeof(height));
        int res = 0;
        for(int i=0;i<m;i++){
            // 遍历每一行
            int cur_left = 0;
            int cur_right = n-1;
            for(int j=n-1;j>=0;j--){
                if(matrix[i][j] == '1'){
                    right[j] = min(cur_right, right[j]);
                }else{
                    right[j] = n-1;
                    cur_right = j-1;
                }
            }
            for(int j=0;j<n;j++){
                if(matrix[i][j] == '1'){
                    left[j] = max(left[j], cur_left);
                    height[j] = height[j] + 1;
                }else{
                    left[j] = 0;
                    cur_left = j+1;
                    height[j] = 0;
                }
                res = max(res, (right[j] - left[j] + 1) * height[j]);
            }
            for(int j=0;j<n;j++){
                cout<<left[j]<<",";
            }
            cout<<endl;
            for(int j=0;j<n;j++){
                cout<<right[j]<<",";
            }
            cout<<endl;
            for(int j=0;j<n;j++){
                cout<<height[j]<<",";
            }
            cout<<endl;
            cout<<res<<endl<<endl;
            cout<<endl;
        }
        return res;
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值