Fork me on GitHub

LeetCode-36:有效的数独

本题为LeetCode第36道题,为中等题


判断一个 9x9 的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。

  1. 数字 1-9 在每一行只能出现一次。
  2. 数字 1-9 在每一列只能出现一次。
  3. 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。

image

上图是一个部分填充的有效的数独。

数独部分空格内已填入了数字,空白格用 ‘.’ 表示。

示例 1:

1
2
3
4
5
6
7
8
9
10
11
12
13
输入:
[
["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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
输入:
[
["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 存在, 因此这个数独是无效的。

说明:

  • 一个有效的数独(部分已被填充)不一定是可解的。
  • 只需要根据以上规则,验证已经填入的数字是否有效即可。
  • 给定数独序列只包含数字 1-9 和字符 ‘.’ 。
  • 给定数独永远是 9x9 形式的。

咱们先来理理思路,就判断么,判断行是否有重复元素,再判断列是否有重复元素,再来判断九宫格是否有重复元素

那么首先来看看判断行元素和列元素是否重复,使用HashSet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**
* 判断是否重复
*/
public static boolean not_to_repeat(char[][] a) {
/**
* 判断行和列是否重复
*/
for (int i = 0; i < 9; i++) {
HashSet<Character> row = new HashSet();
HashSet<Character> col = new HashSet();
for (int j = 0; j < 9; j++) {
//判断行是否重复
if(a[i][j]!='.') {
if (row.contains(a[i][j])) {
return false;
} else {
row.add(a[i][j]);
}
}
if(a[j][i]!='.') {
//判断列是否重复
if (col.contains(a[j][i])) {
return false;
} else {
col.add(a[j][i]);
}
}
}
}
//行和列都无重复
return true;
}

利用HashSet的特性

那么如何判断九宫格呢???

image

观察行号规律

第0个九宫格:000111222; 第1个九宫格:000111222; 第2个九宫格:000111222;

第3个九宫格:333444555; 第4个九宫格:333444555; 第5个九宫格:333444555;

第6个九宫格:666777888; 第7个九宫格:666777888; 第8个九宫格:666777888;

可见对于每三个九宫格行号增3;对于单个九宫格,每三个格点行号增1。

因此第i个九宫格的第j个格点的行号可表示为i/3*3+j/3(每个小九宫格j都是从0~9递增)

观察列号规律

第0个九宫格:012012012; 第1个九宫格:345345345; 第2个九宫格:678678678;

第3个九宫格:012012012; 第4个九宫格:345345345; 第5个九宫格:678678678;

第6个九宫格:012012012; 第7个九宫格:345345345; 第8个九宫格:678678678;

可见对于下个九宫格列号增3,循环周期为3;对于单个九宫格,每个格点行号增1,周期也为3。

周期的数学表示就是取模运算mod。

因此第i个九宫格的第j个格点的列号可表示为i%3*3+j%

所以来看看最终代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
* @param board
* @return
*/
public boolean isValidSudoku(char[][] board) {
for (int i = 0; i < 9; i++) {
HashSet<Character> row = new HashSet<>();
HashSet<Character> column = new HashSet<>();
HashSet<Character> cube = new HashSet<>();
for (int j = 0; j < 9; j++) {
// 检查第i行,在横坐标位置
if (board[i][j] != '.' && !row.add(board[i][j]))
return false;
// 检查第i列,在纵坐标位置
if (board[j][i] != '.' && !column.add(board[j][i]))
return false;
// 行号+偏移量
int RowIndex = 3 * (i / 3) + j / 3;
// 列号+偏移量
int ColIndex = 3 * (i % 3) + j % 3;
//每个小九宫格,总共9个
if (board[RowIndex][ColIndex] != '.' && !cube.add(board[RowIndex][ColIndex]))
return false;
}
}
return true;

}

HashSet集合的add方法,如果添加的元素在集合内,则返回一个false