Given a 2D binary matrix filled with 0's and 1's, find the largest square containing all 1's and return its area.
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 0Return 4.
做完这题最大的收获就是加深了对动态规划省空间的理解。
参考这里的解法点击打开链接
题目要求求面积, 面积是由周长决定的,首先建立一个二维数组,int【】【】dp ,dp[i][j]的值对应的是matrix【i】【j】位置上所对应的点能构成的最大正方形的边长,
对应上图得到的dp【】【】结果为
1 0 1 0 0 1 0 1 1 1 1 1 1 2 2 1 0 0 1 0用一个变量track dp【】【】的最大值 2, 得到最大面积为 2 * 2.
matrix的第一行和第一列是可以初始化对应dp二维数组的,如果有1的话, 则可以构成边长为1的正方形, matrx中的0对应到dp都是0,
对于 i > 1, j > 1(假设都在界内), 则能否构成正方形要看 当前元素 matrix[i][j] 的左边节点, 上边节点, 和左上角的节点, 如果这三个点中有一个是0, 而当前点是1, 则dp[i][j] = 1, 如果这三个点都不为0, 则dp[i][j] = math.min(dp[i][j - 1],dp[i - 1][j], dp[i - 1][j -1]) + 1. 因为边长又多了1, 这个公式对于三点中有0,当前值是1同样适用。
对应代码:
public class Solution {
public int maximalSquare(char[][] matrix) {
if(matrix == null || matrix.length == 0 || matrix[0].length == 0){
return 0;
}
int[][] dp = new int[matrix.length][matrix[0].length];
int maxLen = 0;
for(int i = 0; i < matrix.length; i++){
dp[i][0] = Character.getNumericValue(matrix[i][0]);
if(dp[i][0] > maxLen){
maxLen = dp[i][0];
}
}
for(int i = 0; i < matrix[0].length; i++){
dp[0][i] = Character.getNumericValue(matrix[0][i]);
if(dp[0][i] > maxLen){
maxLen = dp[0][i];
}
}
for(int i = 1; i < matrix.length; i++){
for(int j = 1; j < matrix[0].length; j++){
if(matrix[i][j] == '0'){
dp[i][j] = 0;
}else{
// dp[i][j] = Math.min(Math.min(Character.getNumericValue(matrix[i][j - 1]), Character.getNumericValue(matrix[i - 1][j])), Character.getNumericValue(matrix[i - 1][j - 1])) + 1;
dp[i][j] = Math.min(Math.min(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1;
if(dp[i][j] > maxLen){
maxLen = dp[i][j];
}
}
}
}
return maxLen * maxLen;
}
}
这里有一个地方我疏忽了, 给的matrix是 char【】【】, 想当然的认为成了int【】【】。。。
然后想到优化这个dp, 因为你每次更新dp值的时候只需要知道当前行前一列的值, 前一行的当前值, 以及前一行的前一列的值, 即
dp[i][j - 1], dp[i - 1][j], dp[i - 1][j - 1]
所以用两个一位数组分别记录当前行和前一行的值就可以了。
</pre></p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 10px; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 30px;">对应代码:</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 10px; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 30px;"><pre name="code" class="java">public class Solution {
public int maximalSquare(char[][] matrix) {
if(matrix == null || matrix.length == 0 || matrix[0].length == 0){
return 0;
}
int[] dp = new int[matrix[0].length];
int maxLen = 0;
// for(int i = 0; i < matrix.length; i++){
// dp[i][0] = Character.getNumericValue(matrix[i][0]);
// if(dp[i][0] > maxLen){
// maxLen = dp[i][0];
// }
// }
// int tmpPre = 0; 本来是想用一个变量来存储 top-left 的值, 其实可以用另一个二维数组来存上一排的信息, 这样两个一位数组就可以了, 也是一种空间的优化。
int[] pre = new int[matrix[0].length];
for(int i = 0; i < matrix[0].length; i++){
dp[i] = Character.getNumericValue(matrix[0][i]);
pre[i] = dp[i];
if(dp[i] > maxLen){
maxLen = dp[i];
}
}
for(int i = 1; i < matrix.length; i++){
for(int j = 0; j < matrix[0].length; j++){
if(j == 0){
dp[j] = Character.getNumericValue(matrix[i][j]);
if(dp[j] > maxLen){
maxLen = dp[j];
}
}else{
if(matrix[i][j] == '0'){
dp[j] = 0;
}else{
dp[j] = Math.min(Math.min(dp[j], dp[j - 1]), pre[j - 1]) + 1;
if(dp[j] > maxLen){
maxLen = dp[j];
}
}
}
}
// set curRow dp[] as pre[], so in the next row we still have the info of the cur row.
for(int k = 0; k < dp.length; k++){
pre[k] = dp[k];
}
}
return maxLen * maxLen;
}
}
想了很久怎么用一个变量来代替 pre这个一位数组, 其实每次对新的一行扫描时, 把dp【j】用一个变量tmp存起来, 然后到当前行下一列的时候这个tmp就是dp【i - 1】【j - 1】了, 因为在整个一行的for loop 进行之前, dp【j】中存的值都是上一行的值,
代码如下:
public class Solution {
public int maximalSquare(char[][] matrix) {
if(matrix == null || matrix.length == 0 || matrix[0].length == 0){
return 0;
}
int[] dp = new int[matrix[0].length];
int maxLen = 0;
int lastPre = 0;
for(int i = 0; i < matrix[0].length; i++){
dp[i] = Character.getNumericValue(matrix[0][i]);
if(dp[i] > maxLen){
maxLen = dp[i];
}
}
for(int i = 1; i < matrix.length; i++){
for(int j = 0; j < matrix[0].length; j++){
int tmp = dp[j];
if(j == 0){
dp[j] = Character.getNumericValue(matrix[i][j]);
if(dp[j] > maxLen){
maxLen = dp[j];
}
}else{
if(matrix[i][j] == '0'){
dp[j] = 0;
}else{
dp[j] = Math.min(Math.min(dp[j], dp[j - 1]), lastPre) + 1;
if(dp[j] > maxLen){
maxLen = dp[j];
}
}
}
lastPre = tmp;
}
}
return maxLen * maxLen;
}
}
时间复杂度都是O(m*n), 因为每个点都扫一遍, 空间复杂度从O(m*n)优化到O(2n), 最后是O(n), n为column数