在一个由 0 和 1 组成的二维矩阵内,找到只包含 1 的最大正方形,并返回其面积。
示例:
输入:
1 0 1 0 0
1 0 1 1 1
1 1 1 1 1
1 0 0 1 0输出: 4
思路一:暴力美学
直接从左上角遍历所有元素,遇到1,就开始判断它的最大正方形:
- 设置一个len,初始值为1,代表这个正方形的边长;
- 设置标志位flag,一旦在检测过程中发现了0,就置为false;
- 然后向右和向下遍历此元素的一整行/列(长为len)是否全为1。是则len++,然后继续遍历;否则置标志位为false。
- 最后返回最大的len所对应的面积
时间复杂度:O((mn)2),最坏情况下,我们需要遍历整个矩阵寻找每个 1。
空间复杂度:O(1),没有使用额外的空间。
思路二:动态规划
看到这个题的标签是动态规划时,我就很疑惑,这个题怎么能用动态规划呢,看了看题解才发现,奥~原来是这个意思。
若某格子值为 1 ,则以此为右下角的正方形的最大边长为:上面的正方形、左面的正方形或左上的正方形中,最小的那个,再加上此格。
用图的方法表示:
可以看到,
- 图1:受限于左上的0
- 图2:受限于上边的0
- 图3:受限于左边的0
时间复杂度:O(mn)
空间复杂度:O(mn)
所以,代码如下:
class Solution {
public int maximalSquare(char[][] matrix) {
if(matrix==null || matrix.length==0) return 0;
int nr = matrix.length;
int nc = matrix[0].length;
int max = 0;
//这样的目的是为了避免手动设置边界值
int[][] dp = new int[nr+1][nc+1];
for(int r=1;r<nr+1;r++){
for(int c=1;c<nc+1;c++){
if(matrix[r-1][c-1]=='1'){
dp[r][c] = Math.min(Math.min(dp[r-1][c],dp[r-1][c-1]),dp[r][c-1])+1;
max = Math.max(dp[r][c],max);
}
}
}
return max*max;
}
}
思路三:优化动态规划
这个理解起来,对于我这种白痴而言就更难了。其实我们的遍历是一行一行的遍历的。那么只用一个一维数组dp[]保存一行的边长是否可行呢?
是可行的。每一次换新的一行,都是dp的一次更新换代。那么原来的dp代表的就是上一行。
所以,假如我们用prev来存储上一行的dp[c],来代表下一列左上角的值;然后用dp[c-1]来代表左边的值;用dp[c]来代表上面的值。就可以只用一维数组就可以表示了!!
这样一来,空间复杂度仅为O(n)。
class Solution {
public int maximalSquare(char[][] matrix) {
if(matrix==null || matrix.length==0) return 0;
int nr = matrix.length;
int nc = matrix[0].length;
int max = 0;
int prev = 0;
//这样的目的是为了避免手动设置边界值
int[] dp = new int[nc+1];
for(int r=1;r<nr+1;r++){
for(int c=1;c<nc+1;c++){
int temp = dp[c]; //存储下来,留给下一列用
if(matrix[r-1][c-1]=='1'){
dp[c] = Math.min(Math.min(dp[c-1],prev),dp[c])+1;
max = Math.max(dp[c],max);
}else{
dp[c] = 0;
}
prev = temp;
}
}
return max*max;
}
}