一、题目
请你判断一个 9 x 9
的数独是否有效。只需要 根据以下规则 ,验证已经填入的数字是否有效即可。
- 数字
1-9
在每一行只能出现一次。 - 数字
1-9
在每一列只能出现一次。 - 数字
1-9
在每一个以粗实线分隔的3x3
宫内只能出现一次。(请参考示例图)
注意:
- 一个有效的数独(部分已被填充)不一定是可解的。
- 只需要根据以上规则,验证已经填入的数字是否有效即可。
- 空白格用
'.'
表示。
二、解题思路
1. 准备阶段:
- 初始化三个二维布尔数组
rows
、cols
和boxes
,每个数组都是 9x10 的大小。这些数组将用于跟踪数独板中的行、列和3x3子网格中哪些数字已经出现。 - 确定每个单元格所属的3x3子网格。由于数独板被分为9个3x3的子网格,每个子网格由其左上角的单元格的行索引和列索引来确定。
2. 遍历数独板:
- 遍历数独板的每一个单元格。对于每个单元格:
- 如果单元格为空(即值为'.'),则跳过,因为空单元格表示该位置尚未填入数字,不影响数独的有效性验证。
- 如果单元格包含数字,则将该数字转换为整数。字符 '1' 到 '9' 对应整数 1 到 9,通过简单地从字符中减去 '0' 的ASCII码值得到。
3. 验证数字的唯一性:
- 对于非空单元格中的数字,检查它是否已经出现在其所在的行、列和3x3子网格中:
- 计算3x3子网格的索引,通过
(i / 3) * 3 + j / 3
实现。 - 如果数字在行、列或子网格中的任何一个对应的布尔数组位置已经标记为
true
,则说明该数字已经出现过,数独板无效,返回false
。
- 计算3x3子网格的索引,通过
4. 标记数字的出现:
- 如果数字在行、列和子网格中都是唯一的,则在相应的
rows
、cols
和boxes
数组位置标记为true
,表示该数字已经出现。
5. 完成验证:
- 如果遍历完所有单元格后没有发现任何重复的数字,则数独板有效,返回
true
。
三、具体代码
public class Solution {
public boolean isValidSudoku(char[][] board) {
// 创建行、列和3x3子网格的布尔数组,用于标记数字是否出现过
boolean[][] rows = new boolean[9][10];
boolean[][] cols = new boolean[9][10];
boolean[][] boxes = new boolean[9][10];
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
char num = board[i][j];
if (num != '.') {
// 计算数字对应的整数,字符 '1' 到 '9' 对应整数 1 到 9
int numValue = num - '0'; // 直接减去 '0' 得到 1 到 9 的整数
// 计算3x3子网格的索引
int boxIndex = (i / 3) * 3 + j / 3;
// 检查当前数字是否已在同一行、列或子网格内出现
if (rows[i][numValue] || cols[j][numValue] || boxes[boxIndex][numValue]) {
return false; // 如果已出现,则数独无效
}
// 标记当前数字在同一行、列和子网格内已出现
rows[i][numValue] = true;
cols[j][numValue] = true;
boxes[boxIndex][numValue] = true;
}
}
}
// 所有检查都通过,数独有效
return true;
}
}
四、时间复杂度和空间复杂度
1. 时间复杂度
- 该算法的主要操作是遍历数独板的每个单元格一次,并对于每个非空单元格,检查它在同一行、列和3x3子网格中是否已经出现过相同的数字。
- 由于数独板的大小是固定的9x9,因此需要进行的比较次数是常数,即对于每个单元格,最多进行3次检查(行、列、子网格)。
- 因此,时间复杂度是O(1),或者说是常数时间复杂度。
- 这是因为算法的运行时间不随输入大小(即数独板的大小)而变化。
2. 空间复杂度
- 为了跟踪每个数字在行、列和3x3子网格中的出现情况,我们使用了三个二维布尔数组,每个数组的大小都是9x10。
- 由于数组的大小不随输入数据(即数独板的内容)变化,而是一个固定的常数,所以空间复杂度也是O(1),或者说是常数空间复杂度。
五、总结知识点
-
二维布尔数组:用于跟踪数独板中每个位置的数字出现情况。每个布尔值代表一个数字是否已经出现在特定的行、列或3x3子网格中。
-
数组初始化:创建了三个9x10的二维布尔数组
rows
、cols
和boxes
,其中每个数组的第二个维度大小为10,这是因为我们需要跟踪数字1到9的出现情况,而数组索引从0开始。 -
遍历二维数组:通过嵌套的for循环遍历数独板的每个单元格,这是处理二维数据结构的常见方法。
-
条件判断:使用
if
语句来检查单元格是否为空('.'),以及单元格中的字符是否为数字,并进行相应的处理。 -
字符与整数的转换:通过
num - '0'
将字符 '1' 到 '9' 转换为整数 1 到 9。这是因为字符在ASCII码表中是连续的,且数字字符与数字的整数值之间存在固定的偏移量。 -
3x3子网格索引计算:通过计算
(i / 3) * 3 + j / 3
来确定当前单元格所属的3x3子网格的索引。这个计算基于数独板被分为9个3x3的子网格,每个子网格的左上角单元格的行索引和列索引可以决定整个子网格的位置。 -
逻辑运算:使用逻辑或运算符
||
来检查一个数字是否已经在同一行、列或子网格中出现过。如果出现过,则返回false
表示数独无效。 -
赋值操作:在确认数字未重复出现后,使用赋值操作
=true
来标记该数字在相应位置的出现。 -
方法返回值:根据上述检查,如果所有数字都满足数独的规则,则最终返回
true
表示数独有效。
以上就是解决这个问题的详细步骤,希望能够为各位提供启发和帮助。
标签:练习,数字,数独板,单元格,网格,3x3,数组,LeetCode,数独 From: https://blog.csdn.net/weixin_62860386/article/details/136691149