今日力扣打卡题比较复杂,写个总结回顾一下。首先题目如下
37. 解数独
编写一个程序,通过已填充的空格来解决数独问题。
一个数独的解法需遵循如下规则:
数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
空白格用 ‘.’ 表示。
一个数独。
答案被标成红色。
Note:
给定的数独序列只包含数字 1-9 和字符 ‘.’ 。
你可以假设给定的数独只有唯一解。
给定数独永远是 9x9 形式的。
- 思路
最开始拿到题理解数独本身的定义需要了解,答案就是把空格都填满,然后行程一个二维数组,并且为一个数独,也是唯一的答案。前阵子刚好做过一个题目判断二维数组是否是数独,因此大体有一些思路,竟然要填充空格,那就必然需要尝试,我们可以发现每填充一个都必须满足当前二维数组是数独,如果不满足,就换数字进行尝试,如果全部尝试完了也不满足,那说明前一个空格填充的数字就不对,这里自然而然的想到了回溯。填充一个然后填充下一个又想到了递归,基本大体的思路就定下来了,那就是回溯+递归。
- 代码
/**
* 核心思想就是递归+回溯,把问题拆解,每次只处理一个空格
*/
boolean find = false;
public void solveSudoku(char[][] board) {
int x = -1, y = -1; // 这是当前递归要处理的坐标置
boolean findSpace = false;
for (int i = 0;i < board.length;i ++) {
for (int j = 0;j < board[0].length;j ++) {
if (board[i][j] == '.') {
x = i;
y = j;
findSpace = true;
break;
}
}
if (findSpace) break;
}
if (x == -1 && y == -1) {
find = true;
return ; // 此处说明已经没有空格,找到了答案
}
for (int i = 1;i <= 9;i ++) {
board[x][y] = (char) ('0' + i); // 给当前的这个空格赋值,可能的值来遍历
if (isValidSudo(board)) { // 判断是否是合法的数独
solveSudoku(board); // 继续递归
if (find) return; // 找到答案,不再需要递归
}
}
board[x][y] = '.'; // 回溯,当前任何值都无法组成答案
}
// 判断数独是否成立
public boolean isValidSudo(char[][] board) {
boolean[][] row = new boolean[9][9];
boolean[][] list = new boolean[9][9];
boolean[][] box = new boolean[9][9];
for (int i = 0;i < board.length;i ++) {
for (int j = 0;j < board[0].length;j ++) {
if (board[i][j] == '.') continue;
int num = board[i][j] - '1';
int boxIndex = i / 3 * 3 + j / 3;
if (row[i][num] || list[j][num] || box[boxIndex][num]) return false;
row[i][num] = true;
list[j][num] = true;
box[boxIndex][num] = true;
}
}
return true;
}
这个题的考察点对于数独的理解,难点在于找出递归出入口、回溯、与对于数独的判断。