问题描述
在一个由字符 '0'
和 '1'
组成的二维矩阵中,找到只包含 '1'
的最大正方形,并返回其面积。
例如:
-
示例 1:
- 输入:
matrix = [ ["1","0","1","0","0"], ["1","0","1","1","1"], ["1","1","1","1","1"], ["1","0","0","1","0"] ]
- 输出:
4
解释:最大正方形的边长为 2,面积 2×2 = 4。
- 输入:
-
示例 2:
- 输入:
matrix = [ ["0","1"], ["1","0"] ]
- 输出:
1
- 输入:
-
示例 3:
- 输入:
matrix = [["0"]]
- 输出:
0
- 输入:
第一部分:基础知识
1. 二维矩阵
- 二维矩阵可以理解为由行和列组成的表格。在 Java 中,我们通常使用二维数组来表示它,例如:
char[][] matrix = { {'1', '0', '1', '0', '0'}, {'1', '0', '1', '1', '1'}, {'1', '1', '1', '1', '1'}, {'1', '0', '0', '1', '0'} };
- 每个元素可以通过
matrix[i][j]
访问,其中i
表示行号,j
表示列号。
2. 动态规划(Dynamic Programming,DP)
- 动态规划是一种解决最优化问题的技巧。其思想是将大问题拆解成若干个小问题,并将小问题的解保存下来,避免重复计算。
- 在本题中,我们希望找出以某个位置为右下角的正方形的最大边长,从而推导出全局的最大正方形面积。
3. 状态定义
- 定义二维数组
dp
,其中dp[i][j]
表示以matrix[i][j]
为右下角的正方形的最大边长。 - 如果
matrix[i][j]
是'1'
,那么该位置的正方形边长取决于其上方、左侧和左上角三个位置的正方形边长:dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1;
- 如果
matrix[i][j]
是'0'
,则dp[i][j] = 0
,因为不能构成全为'1'
的正方形。
4. 边界处理
- 对于第一行和第一列,由于没有上方或左侧的元素,所以直接令:
这里的dp[i][j] = matrix[i][j] - '0';
matrix[i][j] - '0'
是将字符'1'
或'0'
转换为数字 1 或 0。
第二部分:题目解题思路
1. 构建 DP 数组
- 首先,建立与
matrix
大小相同的二维整数数组dp
。 - 遍历矩阵:
- 如果在第一行或第一列,直接赋值为
matrix[i][j] - '0'
; - 如果
matrix[i][j]
为'1'
,则:dp[i][j] = Math.min(Math.min(dp[i-1][j], dp[i][j-1]), dp[i-1][j-1]) + 1;
- 否则,
dp[i][j] = 0
。
- 如果在第一行或第一列,直接赋值为
2. 更新最大边长
- 在遍历过程中,不断更新一个变量
maxSide
,记录遇到的最大正方形边长。
3. 计算面积
- 最后,正方形的面积为
maxSide * maxSide
。
第三部分:Java 代码实现
下面是完整的 Java 实现代码,并附有详细注释:
public class Solution {
public int maximalSquare(char[][] matrix) {
int m = matrix.length; // 行数
if (m == 0) return 0; // 如果矩阵为空,返回 0
int n = matrix[0].length; // 列数
int[][] dp = new int[m][n]; // dp 数组,存放以 (i, j) 为右下角的最大正方形边长
int maxSide = 0; // 记录最大正方形边长
// 遍历整个矩阵
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
// 如果在第一行或第一列,只能取矩阵中的值
if (i == 0 || j == 0) {
dp[i][j] = matrix[i][j] - '0'; // 转换字符 '1' 或 '0' 为数字 1 或 0
} else if (matrix[i][j] == '1') {
// 状态转移:以 (i, j) 为右下角的正方形边长取决于其上、左、左上三个位置的最小值
dp[i][j] = Math.min(Math.min(dp[i-1][j], dp[i][j-1]), dp[i-1][j-1]) + 1;
} else {
dp[i][j] = 0; // 如果当前为 '0',则不能构成正方形
}
// 更新最大正方形的边长
maxSide = Math.max(maxSide, dp[i][j]);
}
}
// 返回最大正方形的面积
return maxSide * maxSide;
}
// 主方法用于测试
public static void main(String[] args) {
Solution sol = new Solution();
char[][] matrix1 = {
{'1','0','1','0','0'},
{'1','0','1','1','1'},
{'1','1','1','1','1'},
{'1','0','0','1','0'}
};
System.out.println("最大正方形面积: " + sol.maximalSquare(matrix1));
// 预期输出: 4
char[][] matrix2 = {
{'0','1'},
{'1','0'}
};
System.out.println("最大正方形面积: " + sol.maximalSquare(matrix2));
// 预期输出: 1
char[][] matrix3 = {
{'0'}
};
System.out.println("最大正方形面积: " + sol.maximalSquare(matrix3));
// 预期输出: 0
}
}
第四部分:详细解析代码与动态规划
1. 状态定义
- 状态
dp[i][j]
: 表示以(i, j)
为右下角的、只包含'1'
的正方形的最大边长。
2. 状态转移方程
-
对于
matrix[i][j] == '1'
:dp[i][j] = Math.min(Math.min(dp[i-1][j], dp[i][j-1]), dp[i-1][j-1]) + 1;
这表示:要构成一个以
(i, j)
为右下角的正方形,要求:(i-1, j)
位置的正方形边长;(i, j-1)
位置的正方形边长;(i-1, j-1)
位置的正方形边长;
三者的最小值再加 1,就是当前位置的最大正方形边长。
-
如果
matrix[i][j]
为'0'
,则dp[i][j]
为 0,因为当前位置不能构成全为'1'
的正方形。
3. 边界条件
- 对于第一行和第一列,由于没有上边或左边的元素,我们直接将
dp[i][j]
设为matrix[i][j] - '0'
。
4. 动态规划的思路
- 我们从左上角开始填充
dp
数组,逐行逐列计算每个位置的最大正方形边长,并同时更新全局最大边长maxSide
。 - 最终,最大正方形的面积就是
maxSide * maxSide
。
总结
-
基础知识:
- 二维矩阵的表示方法;
- 动态规划的基本思想和状态转移方程。
-
问题思路:
- 定义状态
dp[i][j]
表示以(i, j)
为右下角的最大正方形边长; - 使用状态转移方程计算每个位置的值,并更新最大边长。
- 定义状态
-
Java 实现:
- 通过两重循环填充
dp
数组,最后计算最大正方形面积。
- 通过两重循环填充