37. 解数独
编写一个程序,通过填充空格来解决数独问题。
数独的解法需 遵循如下规则:
数字 1-9
在每一行只能出现一次。
数字 1-9
在每一列只能出现一次。
数字 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"]]
输出:
[["5","3","4","6","7","8","9","1","2"],
["6","7","2","1","9","5","3","4","8"],
["1","9","8","3","4","2","5","6","7"],
["8","5","9","7","6","1","4","2","3"],
["4","2","6","8","5","3","7","9","1"],
["7","1","3","9","2","4","8","5","6"],
["9","6","1","5","3","7","2","8","4"],
["2","8","7","4","1","9","6","3","5"],
["3","4","5","2","8","6","1","7","9"]]
解释: 输入的数独如上图所示,唯一有效的解决方案如下所示:
沿用上一题的思路👉LeetCode 36. 有效的数独。
依旧使用三个数组分别表示图中某点(x,y)
在行、列、宫内是否出现过。
显然题目的答案是一个关于填入数独图中的空白的组合决策,所以单独研究这些空白的点,并将其存入到blank
的List
中,然后考虑使用深搜,每个空都可枚举一遍1~9
之间的数。
如果满足要求(数独的规则)就接着移到下一个空白处(dfs(deep + 1)
),直到dfs
的深度与空白的点的个数相同,表明此时已到达终点,可以退出。
问题是数独的解是唯一的,即dfs
有一个分支到达终点得解后board
就不允许再被改动了,所以其他分支就得停止搜索。
为了解决这个问题,引入一个终点标记flag,初始的值为false
,然后在每次枚举1~9
的时候检验一遍是否满足!flag
,当深搜到终点时置其值为true
,这样就可以使其他分支执行for
循环时自动退出return
(剪枝)。
Java代码
class pii{ //空白的点
int first;
int second;
public pii(int first_, int second_){
first = first_;
second = second_;
}
}
class Solution {
private List<pii> blank;
private boolean[][] row;
private boolean[][] col;
private boolean[][][] g;
private boolean flag;
public void init(char[][] board){
for(int i = 0;i < 9;i ++)
for(int j = 0;j < 9;j ++){
char c = board[i][j];
if(c == '.')
blank.add(new pii(i, j));
else{
row[i][c - '0'] = true;
col[j][c - '0'] = true;
g[i / 3][j / 3][c - '0'] = true;
}
}
}
public void dfs(int deep, char[][] board){
if(deep == blank.size()){
flag = true; //当找到了解,则flag标记可以终止掉其他dfs()函数执行时的for循环(不断回溯)
return;
}
pii node = blank.get(deep);
for(int i = 1;i <= 9 && !flag;i ++){
int x = node.first, y = node.second;
if(!row[x][i] && !col[y][i] && !g[x / 3][y / 3][i]){
row[x][i] = col[y][i] = g[x / 3][y / 3][i] = true;
board[x][y] = (char) (i + '0'); //更改状态
dfs(deep + 1, board);
row[x][i] = col[y][i] = g[x / 3][y / 3][i] = false; //撤销状态
}
}
}
public void solveSudoku(char[][] board) {
blank = new ArrayList<>();
row = new boolean[10][10];
col = new boolean[10][10];
g = new boolean[3][3][10];
flag = false;
init(board);
dfs(0, board);
}
}