一、写在前面的废话(写给自己以后看)
(1)已经有两个多月的时间没写了,这意味着这两个月没什么值得记录的东西,也意味着没怎么写代码,不好的征兆,要加油了!
(2)做这题的时候,各种乱七八糟的思路满天飞,结果没有一个是对的,折腾一晚上,失败告终,然后向大神请教,终得一解法(发现自己的思路错在了最后一步上,开心又难过)
二、题目
Given a 2D binary matrix filled with 0’s and 1’s, find the largest rectangle containing only 1’s and return its area.
给一个 2 维数组,每个点不是 0 就是 1 ,找到最大的只包含 1 的矩形,并返回最大的面积。
例子:
For example, given the following matrix:
1 0 1 0 0
1 0 1 1 1
1 1 1 1 1
1 0 0 1 0
Return 6.
三、解法
这题,网上有 n2 解法,不过,还没琢磨明白,先记一下当前懂的方法(能跑通LeetCode所有测试用例),后面再继续填坑。
暴力求解就不考虑了,写出来也肯定通不过测试数据量比较大时的测试用例,鉴于暴力求解的复杂度是 n4 (假设输入为n*n的矩阵),想一想能不能有 n3 的解法。
解法1: n3 解法
大体思路:
- 以每一行为子矩形的下底边,遍历每一个可能的底边,然后求出以该边为底边的矩形高最大是多大,最终,矩形面积=底边长度*高度,取最大即可。
- 那么,这个高度该怎么求呢?可以维护一个二维数组存储以当前点为底,往上搜索有多少个连续的 1 ,即为当前点的高度。
- 其实这个想法也就是,固定底边,找最大高。
代码思路:
- new一个二维数组 scanning_column 用来存储每个结点的高度
维护 scanning_column 数组
- 从上到下,从左到右扫描测试用例数组的每一个结点
- 如果该节点是‘0’,则 scanning_column 相应位置 = 0
- 如果该节点非‘0’,则 scanning_column 相应位置 = 1 + scanning_column 数组中上一个结点值
- 在维护该节点高度值时,如果非‘0’,在更新完高度值后,检查以该节点为子矩形底边右顶点,以向左搜索始终未遇到0的所有点为底边左顶点的底边的最小高度(遇到0即可停止搜索)(着实有点绕,好在代码很简单),此最小高度即为该底边对应的最大子矩形的高度,然后即可求出此子矩形的面积,更新当前最大子矩形面积值。
- 从上到下,从左到右扫描测试用例数组的每一个结点
扫描完毕即可得到结果
代码实现:
Java版
class Solution {
public int maximalRectangle(char[][] matrix) {
if(matrix.length == 0 || matrix[0].length == 0) //matrix为空,直接返回即可
return 0;
int row = matrix.length; //行
int column = matrix[0].length; //列
int[][] scanning_column = new int[row][column];//维护每一个结点高度的二维数组
int max = 0; //最终返回值
for(int i = 0; i < row; i++){
for(int j = 0; j < column; j++){
if(matrix[i][j] == '0'){
scanning_column[i][j] = 0;
}else{
if(i>=1){//非第一行,第一行没有上一行
scanning_column[i][j] = scanning_column[i-1][j]+1;
}else{//第一行,是1还是0直接抄上即可
scanning_column[i][j] = 1;
}
//计算以当前点为底边右下角点,同行左边所有连续非0点为左下角点时可得到的最大矩阵
int min = scanning_column[i][j]; //记录右下角点到左下角点间最小高度
for(int k = j; k >= 0; k--){
if(scanning_column[i][k] == 0 )
break;//遇到0直接跳出此次循环即可
if(scanning_column[i][k] < min){
min = scanning_column[i][k];
}
max = (j-k+1)*min > max ? (j-k+1)*min : max;//更新max值
}
}
}
}
return max;
}
}
Python版(最近在学,练练手)写法跟上面是一样的,只是使用Python的语法而已
class Solution(object):
def maximalRectangle(self, matrix):
""" :type matrix: List[List[str]] :rtype: int """
if len(matrix) == 0 or len(matrix[0]) == 0:
return 0;
row = len(matrix); #行
column = len(matrix[0]); #列
scanning_column = [[0 for i in range(column)] for j in range(row)];
max_area = 0;
for i in range(row):
for j in range(column):
if matrix[i][j] == '0':
scanning_column[i][j] = 0;
else:
if i>=1:
scanning_column[i][j] = scanning_column[i-1][j]+1;
else:
scanning_column[i][j] = 1;
#计算以当前点为右下角点,同行左边所有点为左下角点时可得到的最大矩阵
min_height = scanning_column[i][j]; #记录右下角点到左下角点间最小高度
for k in range(j, -1, -1):
if scanning_column[i][k] == 0:
break;
if scanning_column[i][k] < min_height:
min_height = scanning_column[i][k];
max_area = (j-k+1)*min_height if (j-k+1)*min_height > max_area else max_area;
return max_area;
解法2: n4 解法
为什么要记录一下这个解法呢?
(1)第一版的实现卡死在了最后一个测试用例上,超时了。但是,在努力改成 n3 之后居然意外通过了,有点小兴奋。
(2)一开始分析的时候觉得也是 n3 可解的,但是实际写起代码来才发现是 n4 的,改来改去想变成 n3 的,但最终发现,在我能力范围之内是不可能的,但既然写了,就还是记录一下吧。
大体思路:
- 每相邻的 j (1<=j<=n)行对应列相加,找出当前加和为 j 的连续个数最大值(即最多有几个 j 相邻),当前最大 = j * 连续个数。
- 其实这个想法也就是,固定高,找最长底边。
代码思路:
- 将给的 char 型二维数组转化成 int 型二维数组
- 维护一个 cur_left 值,记录连续 j 的最左边那个 j 的前一个值的下标
- 从第 0 行开始,每次将相邻的 j 行相加,从左到右依次扫描,
- 若加和为 j ,则说明当前列的 j 行均为 1,更新最大值
- 最大值为=max{当前最大, j * 连续个数}
- 若加和不为 j,则说明当前列有 0,高度不为 j ,更新 cur_left 为当前元素下标
- 若加和为 j ,则说明当前列的 j 行均为 1,更新最大值
Java实现:
class Solution {
public int maximalRectangle(char[][] matrix) {
if(matrix.length == 0 || matrix[0].length == 0)
return 0;
int row = matrix.length; //行
int column = matrix[0].length; //列
int[][] mat = new int[row][column];
for(int a = 0; a < row; a++){
for(int b = 0; b < column; b++){
if(matrix[a][b] == '1')
mat[a][b] = 1;
else
mat[a][b] = 0;
}
}
int max = 0;
int cur = 0;
int cur_left = -1;//记录与cur相等的最左边值的前一个值的下标
for(int i = 0; i < row; i++){
for(int j = 1; j <= row-i; j++){ //mat每j行相加,计算出相邻值均为j的个数,j*个数=子矩阵面积
cur_left = -1;
for(int m = 0; m < column; m++){//每j行相加,计算出目前为止最大子矩阵
for(int k = i; k < i+j && k < row; k++){
cur = k == i ? mat[k][m] : (cur + mat[k][m]);
}
if(cur == j){
max = (cur * (m - cur_left)) > max ? (cur * (m - cur_left)) : max;
}else{
cur_left = m;
}
}
}
}
return max;
}
}