一、题目描述
请你判断一个 9x9 的数独是否有效。只需要 根据以下规则 ,验证已经填入的数字是否有效即可。
数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)
数独部分空格内已填入了数字,空白格用 ‘.’ 表示。
注意:
一个有效的数独(部分已被填充)不一定是可解的。
只需要根据以上规则,验证已经填入的数字是否有效即可。
二、解题思路
这道题需要使用一些技巧来计算,题目中给了三个条件,那么我们就直接根据每个条件来直接模拟。
技巧:申请一个9*9
的数组,初始化都是F(false),将原始数组中的每个元素值找到新数组中对应的列索引,如果有元素值就将其改为T,表示出现了一次,什么意思呢?下面举例说明:
第一个条件:数字 1-9 在每一行只能出现一次。
左右两边的行是一一对应的,首先来看第一行的第一个元素5,它的索引应该是4,那么就将右边矩阵第一行对应的索引4(其实也代表第四列)下的F改成T,表示5出现了一次。
再来看第一行的第二个元素3,它的索引应该是2(也相当于第二列),那么就将右边矩阵第一行对应的索引2下的F改成T,表示3出现了一次。依次类推(这里就举两行的例子):
第二个条件:数字 1-9 在每一列只能出现一次。
跟上面一样,只不过这里不在是左边一行对应右边一行,而是左边一列对应右边一行,这里还是只拿初始矩阵的两列作为例子。首先还是来看第一列的第一个元素5,在右边矩阵中还是来看第一行,元素5对应的索引是4,所以将第一行第四列也就是坐标(0,4)改成T,表示5出现了一次。
再来看第一列的第二个元素6,对应的索引是5,所以将第一行第五列也就是坐标(0,5)改成T,表示5出现了一次,以此类推,左边矩阵第二列对应右边矩阵第二行的元素:
第三个条件:数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
怎么划分宫格呢,将每个3*3的块看成整体,第一列从上往下是1,2,3宫格,第二列是4,5,6宫格,第三列是7,8,9宫格,每个宫格1到9的元素对应右边矩阵每一行0到8的索引。
某个元素对应的是哪个宫格有一个计算公式:boxIndex = row/3 + (col/3)*3
首先先来看左边矩阵的第一行元素5和3,这都是第一宫格的,直接就看出来了,不用通过公式计算。第一个宫格对应右边矩阵的第一行,那么将第一行元素的第4和第2列位置改成T,说明5和3个出现了一次。
再来看左边矩阵的元素7,它的坐标是row=0,col=4,通过上面的公式boxIndex =0/3+(4/3)*3 = 3
,说明该元素出现在第四个宫格,对应右边矩阵的第四行,所以将右边矩阵第四行六列改为T。
依次类推,这里就以左边矩阵前三行为例:
三、代码演示
class Solution {
public boolean isValidSudoku(char[][] board) {
//首先初始化三个9*9的数组
//表示每一行1-9出现次数的情况
boolean[][] rowUsed = new boolean[9][9];
//表示每一列1-9出现次数的情况
boolean[][] colUsed = new boolean[9][9];
//表示每一宫格1-9出现次数的情况
boolean[][] boxUsed = new boolean[9][9];
//遍历数组
for (int row=0; row<board.length; row++){
for (int col=0; col<board[0].length; col++){
//如果当前元素是数字
if (board[row][col] != '.'){
//先将字符类型转成int类型
int num = board[row][col] - '1';
//判断这个数字在新数组中的行是否出现了
if (rowUsed[row][num]){
return false;
}else {
rowUsed[row][num] = true;
}
//判断这个数字在新数组中的行是否出现了
if (colUsed[col][num]){
return false;
}else {
colUsed[col][num] = true;
}
//判断宫格
int boxIndex = row/3 + (col/3)*3;
if (boxUsed[boxIndex][num]){
return false;
}else {
boxUsed[boxIndex][num] = true;
}
}
}
}
return true;
}
}