LeetCode37.解数独
题目描述
/**
* 编写一个程序,通过填充空格来解决数独问题。
* <p>
* 数独的解法需 遵循如下规则:
* <p>
* 数字 1-9 在每一行只能出现一次。
* 数字 1-9 在每一列只能出现一次。
* 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)
* 数独部分空格内已填入了数字,空白格用 '.' 表示。
*/
思路分析
- 解数独,即在满足三个条件的情况下在二维数组中填充数字,即每行只能出现一次,每列只能出现一次,每个子序列中只能出现一次,因此可以使用递归+回溯的思路
- 分别使用三个数组保存保存每行每列和每个子序列中的元素是否满足条件
- 先扫描一遍二维数组,记录二维数组中非空的位置,因为二维数组中有些位置已经填充好元素,使用一个保存数组的集合保存可以填充元素的横纵坐标位置
- 使用深度优先的递归方式,定义一个布尔变量判断二维数组中的所有位置是否全部填充数组,如果全部填充,则结束
- 否则向当前位置填充数字,使用循环,从1到9依次判断当前位置是否能填充,如果能填充,则将该位置置为已经填充的状态,并且判断下一个位置,如果到某一位置后发现不能再填充,则回溯
- 源码见下
源码及分析
//使用二维数组判断每一行的数字是否不重复
private boolean[][] row = new boolean[9][9];
//判断每一列的数组是否步重复
private boolean[][] col = new boolean[9][9];
//判断每一个子序列元素是否不重复
private boolean[][][] box = new boolean[3][3][9];
//判断所有的位置是否填充完毕
private boolean valid = false;
//记录需要填充数字的位置
private List<int[]> space = new ArrayList<>();
public void solveSudoku(char[][] board) {
//遍历二维数组
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[i].length; j++) {
//如果当前位置是一个 . 说明需要填充数字
if (board[i][j] == '.') {
space.add(new int[]{i, j});
} else {
//如果是一个数字,则将其所在位置置为不可填充
int digit = board[i][j] - '0' - 1;
row[i][digit] = col[j][digit] = box[i / 3][j / 3][digit] = true;
}
}
}
dfs(board, 0);
}
/**
* @param board 二维数组
* @param pos 二维数组中的某一位置
*/
public void dfs(char[][] board, int pos) {
//如果所有的位置都被填充完
if (pos == space.size()) {
valid = true;
return;
}
//否则递归的向该位置填充 1 - 9数字
int[] positon = space.get(pos);
//先获取要填充位置的行和列坐标
int i = positon[0], j = positon[1];
for (int digit = 0; digit < 9 && !valid; digit++) {
//如果digit + 1这个数字在行和列和子序列都没有填充
if (!row[i][digit] && !col[j][digit] && !box[i / 3][j / 3][digit]) {
//先将三个数组相应的位置置为true
row[i][digit] = col[j][digit] = box[i / 3][j / 3][digit] = true;
//再将数字字符填充到二维数组
board[i][j] = (char) (digit + '0' + 1);
//递归填充下一个位置
dfs(board, pos + 1);
//如果填充失败,则回溯,同时将这个填充失败的位置置为false。表示可以再次填充
row[i][digit] = col[j][digit] = box[i / 3][j / 3][digit] = false;
}
}
}