求解9*9数独
题目说明:
通过填充空格来解决数独问题。
一个数独的解法需遵循如下规则:
数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
空白格用 ‘.’ 表示。
一个数独:
答案被标成红色:
提示:
1.给定的数独序列只包含数字 1-9 和字符 '.' 。
2.你可以假设给定的数独只有唯一解。
3.给定数独永远是 9x9 形式的。
问题分析:
可以考虑按照**“行优先”的顺序依次枚举每一个空白格中填的数字,通过递归 + 回溯**的方法枚举所有可能的填法。当递归到最后一个空白格后,如果仍然没有冲突,说明找到了答案;在递归的过程中,如果当前的空白格不能填下任何一个数字,进行回溯。
由于每个数字在同一行、同一列、同一个九宫格中只会出现一次,可以使用 row[i],col[j],blo[x][y]分别表示第 i 行,第 j 列,第 (x,y)个九宫格中填写数字的情况。
递归思路:
用一个数组记录每个数字是否出现,可以填写的数字范围为 [1,9],数组的下标从 000 开始,在存储时,使用一个长度为 999 的布尔类型的数组,其中 i 个元素的值为 True,当且仅当数字 i+1 出现过。
当遍历到第 i 行第 j 列的位置:
如果该位置是一个空白格,将其加入一个用来存储空白格位置的列表中,方便后续的递归操作;
如果该位置是一个数字 x,需要将 row[i][x−1],col[j][x−1], blo[⌊i/3⌋][⌊j/3⌋][x−1]置为 True。
算法求解部分如下:
class Solution {
private boolean[][] row=new boolean[9][9];
private boolean[][] col=new boolean[9][9];
private boolean[][][] blo=new boolean[3][3][9];
private boolean valid=false;
private List<int[]> spaces=new ArrayList<>();
public void solveSudoku(char[][] board) {
//递归
for(int i=0;i<9;i++){
for(int j=0;j<9;j++){
if(board[i][j]=='.'){
//保存空位置
spaces.add(new int[]{i,j});
}
//在该位置数字三个区间标记
else{
int dig=board[i][j]-'0'-1;
row[i][dig]=col[j][dig]=blo[i/3][j/3][dig]=true;
}
}
}
//第一个空位置开始值的填入
dfs(board,0);
}
private void dfs(char[][] board,int pos){
if(pos==spaces.size()){
valid=true;
return ;
}
int[] space=spaces.get(pos);
int i=space[0],j=space[1];
//遍历判断该值是符合
for(int dig=0;dig<9 && !valid;dig++){
if(!row[i][dig] && !col[j][dig] && !blo[i/3][j/3][dig]){
row[i][dig]=col[j][dig]=blo[i/3][j/3][dig]=true;
board[i][j]=(char)(dig+'0'+1);
//下一个空值位置
dfs(board,pos+1);
//递归之后的值恢复
row[i][dig]=col[j][dig]=blo[i/3][j/3][dig]=false;
}
}
}
}