LeetCode37-解数独

题目链接

英文链接:https://leetcode.com/problems/sudoku-solver/

中文链接:https://leetcode-cn.com/problems/sudoku-solver/

题目详述

编写一个程序,通过已填充的空格来解决数独问题。

一个数独的解法需遵循如下规则

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

空白格用 '.' 表示。

img

一个数独。

img

答案被标成红色。

Note:

  • 给定的数独序列只包含数字 1-9 和字符 '.'
  • 你可以假设给定的数独只有唯一解。
  • 给定数独永远是 9x9 形式的。

题目详解

DFS,回溯。

  • 用三个数组分别记录某行的某位数字是否已经被摆放、某列的某位数字是否已经被摆放、3x3 宫格内的某位数字是否已经被摆放。
  • 初始化这三个访问标志。
  • 进入下一层前设置访问标志,并改变当前状态。
  • 进入下一层。
  • 退出下一层后进行恢复。
  • 成功的条件是整个棋盘被正确设置,也就是沿着一个一个格子移动后跃出棋盘。
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
public class LeetCode_00037 {

public void solveSudoku(char[][] board) {
// 记录某行的某位数字是否已经被摆放
boolean[][] row = new boolean[9][9];
// 记录某列的某位数字是否已经被摆放
boolean[][] col = new boolean[9][9];
// 记录 3x3 宫格内的某位数字是否已经被摆放
boolean[][] block = new boolean[9][9];
for (int i = 0; i < 9; ++i) {
for (int j = 0; j < 9; ++j) {
if (board[i][j] != '.') {
int num = board[i][j] - '1';
row[i][num] = true;
col[j][num] = true;
// 化二维的 3x3 为一维的 0~8
block[i / 3 * 3 + j / 3][num] = true;
}
}
}
dfs(board, 0, 0, row, col, block);
}

private boolean dfs(char[][] board, int i, int j, boolean[][] row, boolean[][] col, boolean[][] block) {
while (board[i][j] != '.') {
if (++j == 9) {
++i;
j = 0;
}
if (i == 9) {
return true;
}

}
// 化二维的 3x3 为一维的 0~8
int blockIndex = i / 3 * 3 + j / 3;
for (int k = 0; k < 9; ++k) {
if (!row[i][k] && !col[j][k] && !block[blockIndex][k]) {
// 设置访问标志
row[i][k] = true;
col[j][k] = true;
block[blockIndex][k] = true;
// 改变当前值
board[i][j] = (char) ('1' + k);
// 进入下一层,成功则直接返回
if (dfs(board, i, j, row, col, block)) {
return true;
}
// 恢复
row[i][k] = false;
col[j][k] = false;
block[blockIndex][k] = false;
board[i][j] = '.';
}
}
return false;
}
}