85. Maximal Rectangle

来源:互联网 发布:shell curl post json 编辑:程序博客网 时间:2024/05/29 12:31

题目:

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 0r: 7 7 7 4 7 7 7

row 1:

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

row 2:

l: 0 1 2 3 2 1 0r: 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
原创粉丝点击