85. Maximal Rectangle

题目:

Given a 2D binary matrix filled with 0's and 1's, find the largest rectangle containing all ones and return its area.

题意:

给定一个充满0和1的二维矩阵,找到包含最多1的矩形并返回其面积。

思路一:转载地址:http://blog.csdn.net/linhuanmars/article/details/24444491

原文:原博文地址

这是一道非常综合的题目,要求在0-1矩阵中找出面积最大的全1矩阵。刚看到这道题会比较无从下手,brute force就是对于每个矩阵都看一下,总共有m(m+1)/2*n(n+1)/2个子矩阵(原理跟字符串子串类似,字符串的子串数有n(n+1)/2,只是这里是二维情形,所以是两个相乘),复杂度相当高,肯定不是面试官想要的答案,就不继续想下去了。 
这道题的解法灵感来自于 Largest Rectangle in Histogram 这道题,假设我们把矩阵沿着某一行切下来,然后把切的行作为底面,将自底面往上的矩阵看成一个直方图(histogram)。直方图的中每个项的高度就是从底面行开始往上1的数量。根据Largest Rectangle in Histogram 我们就可以求出当前行作为矩阵下边缘的一个最大矩阵。接下来如果对每一行都做一次 Largest Rectangle in Histogram ,从其中选出最大的矩阵,那么它就是整个矩阵中面积最大的子矩阵。 
算法的基本思路已经出来了,剩下的就是一些节省时间空间的问题了。 
我们如何计算某一行为底面时直方图的高度呢? 如果重新计算,那么每次需要的计算数量就是当前行数乘以列数。然而在这里我们会发现一些动态规划的踪迹,如果我们知道上一行直方图的高度,我们只需要看新加进来的行(底面)上对应的列元素是不是0,如果是,则高度是0,否则则是上一行直方图的高度加1。利用历史信息,我们就可以在线行时间内完成对高度的更新。我们知道, Largest Rectangle in Histogram的算法复杂度是O(n)。所以完成对一行为底边的矩阵求解复杂度是O(n+n)=O(n)。接下来对每一行都做一次,那么算法总时间复杂度是O(m*n)。 
空间上,我们只需要保存上一行直方图的高度O(n),加上 Largest Rectangle in Histogram 中所使用的空间O(n),所以总空间复杂度还是O(n)。代码如下:16ms

public int maximalRectangle(char[][] matrix) {
  if(matrix==null || matrix.length==0 || matrix[0].length==0)
  {
    return 0;
  }
  int maxArea = 0;
  int[] height = new int[matrix[0].length];
  for(int i=0;i<matrix.length;i++)
  {
    for(int j=0;j<matrix[0].length;j++)
    {
      height[j] = matrix[i][j]=='0'?0:height[j]+1;
    }
    maxArea = Math.max(largestRectangleArea(height),maxArea);
  }
  return maxArea;
}
public int largestRectangleArea(int[] height) {
  if(height==null || height.length==0)
  {
    return 0;
  }
  int maxArea = 0;
  LinkedList<Integer> stack = new LinkedList<Integer>();
  for(int i=0;i<height.length;i++)
  {
    
    while(!stack.isEmpty() && height[i]<=height[stack.peek()])
    {
      int index = stack.pop();
      int curArea = stack.isEmpty()?i*height[index]:(i-stack.peek()-1)*height[index];
      maxArea = Math.max(maxArea,curArea);
    }
    stack.push(i);
  }
  while(!stack.isEmpty())
  {
    int index = stack.pop();
    int curArea = stack.isEmpty()?height.length*height[index]:(height.length-stack.peek()-1)*height[index];
    maxArea = Math.max(maxArea,curArea);			
  }
  return maxArea;
}
这道题最后的复杂度是非常令人满意的,居然在O(m*n)时间内就可以完成对最大矩阵的搜索,可以看出这已经是下界(因为每个元素总要访问一下才知道是不是1)了。难度还是比较大的,相信要在面试当场想到这种方法是很不容易的。

思路二:转载地址:https://leetcode.com/discuss/20240/share-my-dp-solution

DP动态规划解决方法:对矩阵每一行进行扫描,找到每行元素的左边界与右边界以及高,取面积最大的。

动态规划的状态转移方程式:

left(i,j) = max(left(i-1,j), curleft), curleft can be determined from the current row

right(i,j) = min(right(i-1,j), curright), curright can be determined from the current row

height(i,j) = height(i-1,j) + 1, if matrix[i][j]=='1';

height(i,j) = 0, if matrix[i][j]=='0'

代码:

class Solution {
public:
    int maximalRectangle(vector<vector<char>>& matrix) {
        
        if(matrix.empty()){
            return 0;
        }
        const int m = matrix.size();
        const int n = matrix[0].size();
        int left[n], right[n], height[n];
        fill_n(left,n,0);
        fill_n(right,n,n);
        fill_n(height,n,0);
        int maxArea = 0;
        for(int i=0; i<m; i++){
            int cur_left = 0;
            int cur_right = n;
            for(int j=0; j<n; j++){  //对应元素的高
                if(matrix[i][j] == '1'){
                    height[j]++;
                }else{
                    height[j] = 0;
                }
            }
            for(int j=0; j<n; j++){ //左边界
                if(matrix[i][j] == '1'){
                    left[j] = max(left[j], cur_left);
                }else{
                    left[j] = 0;
                    cur_left = j+1;
                }
            }
            for(int j=n-1; j>=0; j--){ //右边界
                if(matrix[i][j] == '1'){
                    right[j] = min(right[j], cur_right);
                }else{
                    right[j] = n;
                    cur_right = j;
                }
            }
            for(int j=0; j<n; j++){
                maxArea = max(maxArea, (right[j]-left[j])*height[j]);
            }
        }
        return maxArea;
    }
};

If you think this algorithm is not easy to understand, you can try this example:

0 0 0 1 0 0 0 
0 0 1 1 1 0 0 
0 1 1 1 1 1 0

The vector "left" and "right" from row 0 to row 2 are as follows

row 0:

l: 0 0 0 3 0 0 0
r: 7 7 7 4 7 7 7

row 1:

l: 0 0 2 3 2 0 0
r: 7 7 5 4 5 7 7 

row 2:

l: 0 1 2 3 2 1 0
r: 7 6 5 4 5 6 7

The vector "left" is computing the left boundary. Take (i,j)=(1,3) for example. On current row 1, the left boundary is at j=2. However, because matrix[1][3] is 1, you need to consider the left boundary on previous row as well, which is 3. So the real left boundary at (1,3) is 3.

I hope this additional explanation makes things clearer.


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值