Sudoku Solver (H)
Write a program to solve a Sudoku puzzle by filling the empty cells.
A sudoku solution must satisfy all of the following rules:
- Each of the digits
1-9
must occur exactly once in each row. - Each of the digits
1-9
must occur exactly once in each column. - Each of the the digits
1-9
must occur exactly once in each of the 93x3
sub-boxes of the grid.
Empty cells are indicated by the character '.'
.
A sudoku puzzle…
…and its solution numbers marked in red.
Note:
- The given board contain only digits
1-9
and the character'.'
. - You may assume that the given Sudoku puzzle will have a single unique solution.
- The given board size is always
9x9
.
题意
完成数独。
思路
典型的“试错型”问题,使用回溯法:遍历每个空格,每次先插入一个有效的数字(有效性通过isValid()进行判断),再向后递归,如果递归能够解出数独,说明当前填入的数字是正确的,问题已解决直接返回即可;否则将填入的数字恢复为空格,等待填入下一个数字。不断的“试错”后就能得到正确答案。
代码实现 - 无hash
class Solution {
public void solveSudoku(char[][] board) {
solve(board, 0, 0);
}
private boolean solve(char[][] board, int i, int j) {
// 每一格都填上了数字,说明已经解出数独
if (i == board.length) {
return true;
}
int nextI = j == board.length - 1 ? i + 1 : i;
int nextJ = j == board.length - 1 ? 0 : j + 1;
// 已有数字则不需要处理,直接向后递归
if (board[i][j] != '.') {
return solve(board, nextI, nextJ);
}
for (int k = 1; k <= 9; k++) {
char c = (char) ('0' + k);
if (isValid(board, i, j, c)) {
board[i][j] = c;
boolean solved = solve(board, nextI, nextJ);
// 如果递归能解出数独,说明当前填入的数字是正确的
// 否则恢复原状,以免影响下次循环有效性判断的正确性
if (solved) {
return true;
} else {
board[i][j] = '.';
}
}
}
return false;
}
private boolean isValid(char[][] board, int i, int j, char c) {
for (int k = 0; k < 9; k++) {
// 注意要排除自身
if (k != j && c == board[i][k] || k != i && c == board[k][j]) {
return false;
}
}
// x、y的初始值为对应九宫格左上顶点的坐标
for (int x = 3 * (i / 3); x < 3 * (i / 3) + 3; x++) {
for (int y = 3 * (j / 3); y < 3 * (j / 3) + 3; y++) {
// 注意要排除自身
if (c == board[x][y] && x != i && y != j) {
return false;
}
}
}
return true;
}
}
代码实现 - hash (最先想出的杂技解法hhh)
class Solution {
List<Set<Character>> row = new ArrayList<>();
List<Set<Character>> col = new ArrayList<>();
List<Set<Character>> nine = new ArrayList<>();
public void solveSudoku(char[][] board) {
initialize(board);
solve(board, 0, 0);
}
private boolean solve(char[][] board, int i, int j) {
if (i == board.length) {
return true;
}
int nextI = j == board.length - 1 ? i + 1 : i;
int nextJ = j == board.length - 1 ? 0 : j + 1;
if (board[i][j] != '.') {
return solve(board, nextI, nextJ);
}
for (int k = 1; k <= 9; k++) {
char c = (char) ('0' + k);
if (!row.get(i).contains(c)
&& !col.get(j).contains(c)
&& !nine.get(3 * (i / 3) + j / 3).contains(c)) {
board[i][j] = c;
row.get(i).add(c);
col.get(j).add(c);
nine.get(3 * (i / 3) + j / 3).add(c);
boolean flag = solve(board, nextI, nextJ);
if (flag) {
return true;
} else {
board[i][j] = '.';
row.get(i).remove(c);
col.get(j).remove(c);
nine.get(3 * (i / 3) + j / 3).remove(c);
}
}
}
return false;
}
private void initialize(char[][] board) {
for (int i = 0; i < 9; i++) {
row.add(new HashSet<>());
col.add(new HashSet<>());
nine.add(new HashSet<>());
}
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
char c = board[i][j];
if (c != '.') {
row.get(i).add(c);
col.get(j).add(c);
nine.get(3 * (i / 3) + j / 3).add(c);
}
}
}
}
}