36. 有效的数独
1.题目描述
请你判断一个 9 x 9
的数独是否有效。只需要 根据以下规则 ,验证已经填入的数字是否有效即可。
1.数字 1-9
在每一行只能出现一次。
2.数字 1-9
在每一列只能出现一次。
3.数字 1-9
在每一个以粗实线分隔的 3x3
宫内只能出现一次。(请参考示例图)
注意:
- 一个有效的数独(部分已被填充)不一定是可解的。
- 只需要根据以上规则,验证已经填入的数字是否有效即可。
- 空白格用 ‘.’ 表示。
示例 1:
输入:board =
[[“5”,“3”,“.”,“.”,“7”,“.”,“.”,“.”,“.”]
,[“6”,“.”,“.”,“1”,“9”,“5”,“.”,“.”,“.”]
,[“.”,“9”,“8”,“.”,“.”,“.”,“.”,“6”,“.”]
,[“8”,“.”,“.”,“.”,“6”,“.”,“.”,“.”,“3”]
,[“4”,“.”,“.”,“8”,“.”,“3”,“.”,“.”,“1”]
,[“7”,“.”,“.”,“.”,“2”,“.”,“.”,“.”,“6”]
,[“.”,“6”,“.”,“.”,“.”,“.”,“2”,“8”,“.”]
,[“.”,“.”,“.”,“4”,“1”,“9”,“.”,“.”,“5”]
,[“.”,“.”,“.”,“.”,“8”,“.”,“.”,“7”,“9”]]
输出:true
示例 2:
输入:board =
[[“8”,“3”,“.”,“.”,“7”,“.”,“.”,“.”,“.”]
,[“6”,“.”,“.”,“1”,“9”,“5”,“.”,“.”,“.”]
,[“.”,“9”,“8”,“.”,“.”,“.”,“.”,“6”,“.”]
,[“8”,“.”,“.”,“.”,“6”,“.”,“.”,“.”,“3”]
,[“4”,“.”,“.”,“8”,“.”,“3”,“.”,“.”,“1”]
,[“7”,“.”,“.”,“.”,“2”,“.”,“.”,“.”,“6”]
,[“.”,“6”,“.”,“.”,“.”,“.”,“2”,“8”,“.”]
,[“.”,“.”,“.”,“4”,“1”,“9”,“.”,“.”,“5”]
,[“.”,“.”,“.”,“.”,“8”,“.”,“.”,“7”,“9”]]
输出:false
解释:除了第一行的第一个数字从 5 改为 8 以外,空格内其他数字均与 示例1 相同。 但由于位于左上角的 3x3 宫内有两个 8 存在, 因此这个数独是无效的。
提示:
board.length == 9
board[i].length == 9
board[i][j]
是一位数字(1-9
)或者'.'
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/valid-sudoku著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
2.题解
2.1 哈希表
2.1.1 思路
当数字只能出现一次时,自然而然会想到哈希。因为哈希集的特点就是不能存重复的值,然后本题就用了哈希表来存不同的行,列和框。当遍历到一个值时,就去查对应的行,列和框的哈希集,如果存在那就是重复了;不存在就将该值放进去。
- 首先建立哈希表用来存不同的行,列和框。
- 遍历哈希表,将9个行,列,框先放进去。
- 之后遍历数独,跳过
.
,然后判断哈希集是否有该值,不存在就将该值加入;存在就返回false。 - 如果遍历完整个数独都没有重复的,就返回true。
2.1.2 Java代码
class Solution {
public boolean isValidSudoku(char[][] board) {
HashMap<Integer,Set<Integer>> col = new HashMap<>(),row = new HashMap<>(),box = new HashMap<>();
for(int i=0;i<9;i++){
col.put(i,new HashSet<>());
row.put(i,new HashSet<>());
box.put(i,new HashSet<>());
}
for(int i=0;i<9;i++){
for(int j=0;j<9;j++){
char c = board[i][j];
if(c=='.'){
continue;
}
int number = c-'0';
int boxn = (i/3*3)+j/3;
if(col.get(i).contains(number)||row.get(j).contains(number)||box.get(boxn).contains(number)){
return false;
}
else{
col.get(i).add(number);
row.get(j).add(number);
box.get(boxn).add(number);
}
}
}
return true;
}
}
2.2 数组
2.2.1 思路
前面讲过了用哈希表来存储,但其实可以直接用数组来存是否有该值。因为我是先写的哈希表,所以框的数组没有和官方题解一样用三维数组,直接用了二维。
row[][]
中第一维表示的是哪一行,第二维表示的是该行中有哪个值,列和框以此类推。
如果第i
行第n
个值不为0,那么说明该行出现过该值,则可以返回false。接下来讲代码:
- 首先新建三个二维数组用来存储
- 然后遍历数组,通过数组判断是否出现重复,没出现过就将该值+1;出现就返回
false
- 最后如果没有重复,就返回
true
2.2.2 Java代码
class Solution {
public boolean isValidSudoku(char[][] board) {
int [][]row = new int[9][9];
int [][]col = new int[9][9];
int [][]box = new int[9][9];
for(int i=0;i<9;i++){
for(int j=0;j<9;j++){
char c = board[i][j];
if(c == '.'){
continue;
}
int n = c-'0'-1;
int boxn = i/3*3+j/3;
if(row[i][n]>0||col[j][n]>0||box[boxn][n]>0){
return false;
}
row[i][n]++;
col[j][n]++;
box[boxn][n]++;
}
}
return true;
}
}
2.3 位运算
2.3.1 思路
首先需要了解位运算,但是在这里不展开讲。就直接讲这一题的位运算。
假设出现了数字5
,那么就将1<<5
,也就是1
向左移5
位,就变成了二进制的(0000 0010 0000)
。
如果再出现了数字3
,那也将1<<3
,1
向左移3
位,变成二进制的(0000 0000 1000)
。
通过或操作,也就是|
将两个数放在一起,变成了(0000 0010 1000)
。以此类推。
通过且操作,能够判断&
数组中的数字中是否存在board[i][j]
,比如board[i][j] = 5
,那两者(0000 0010 1000)&(0000 0010 0000)= (0000 0000 0000)
。那就返回false。
介绍完位运算就可以看代码结构:
- 首先新建三个一维数组
- 遍历数独,跳过
.
,之后将值变成位元素之后的值。 - 通过且操作判断是否存在该值,存在就返回
false
,不存在就通过或操作将该值加入。 - 遍历完数独,就返回
true
2.3.2 Java代码
class Solution {
public boolean isValidSudoku(char[][] board) {
int[] row = new int[9];
int[] col = new int[9];
int[] box = new int[9];
for(int i=0;i<9;i++){
for(int j=0;j<9;j++){
char c = board[i][j];
if(c == '.'){
continue;
}
int n = 1<<(c-'0');
int boxn = i/3*3+j/3;
if((row[i]&n)!=0||(col[j]&n)!=0||(box[boxn]&n)!=0){
return false;
}
row[i] |= n;
col[j] |= n;
box[boxn] |= n;
}
}
return true;
}
}