力扣 - 37. 解数独
题目
思路(回溯+递归)
- 用三个数组分别记录行、列、块是否已填入数字
- 在用一个ArrayList数组来存储数组行、列下标
- 从ArrayList的第一个元素开始尝试,如果符合条件,进入下一个数字的填写,如果不符合条件,那么就回溯,直到如果填道ArrayList的最后一个元素,那么就说明全部填完,将valid赋值为true,一路return即可
代码
class Solution {
// 存储board的每一行
boolean[][] row = new boolean[9][9];
// 存储board的每一列
boolean[][] col = new boolean[9][9];
// 存储board的每一块
boolean[][][] block = new boolean[3][3][9];
// 刚开始将valid赋值为false,说明一个都还没开始填
boolean valid = false;
// 存储带填写位置的 行、列 索引
List<int[]> spaces = new ArrayList<>();
public void solveSudoku(char[][] board) {
// 从头到尾,遍历board,如果填写就标记为true,否则将索引存储到spaces
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 {
// num是用来标记该位置是否有数字的,减1是因为num是索引所以要减1
int num = board[i][j] - '0' - 1;
row[i][num] = true;
col[j][num] = true;
// 由于是3*3的块,所以i和j要除以3
block[i/3][j/3][num] = true;
}
}
}
// 递归回溯查找
dfs(board, 0);
}
private void dfs(char[][] board, int pos) {
// 如果遍历到spaces的末尾,说明全部匹配填写成功,然后一return,得到答案
if (spaces.size() == pos) {
// valid赋值为true
valid = true;
return;
}
// 选取在spaces中的pos位置的 i 和 j 索引
int[] temp = spaces.get(pos);
// 将获取的索引数组转换成 i 和 j 下标
int i = temp[0];
int j = temp[1];
// 依次使用1-9填入回溯,!valid说明未找到答案
for (int target = 0; target < 9 && !valid; target++) {
// 该位置只有是未填写才可以
if (!row[i][target] && !col[j][target] && !block[i/3][j/3][target]) {
// 先将该位置改成true,说明填入了数字
row[i][target] = true;
col[j][target] = true;
block[i/3][j/3][target] = true;
board[i][j] = (char) (target + '0' + 1);
// 然后递归进行下一个数字的填写
dfs(board, pos+1);
// 回溯,将刚刚填写过的撤销
row[i][target] = false;
col[j][target] = false;
block[i/3][j/3][target] = false;
}
}
}
}
复杂度分析
- 时间复杂度:\(O(9^81)\)
- 空间复杂度:\(O(3*3*9)\)
我走得很慢,但我从不后退!

浙公网安备 33010602011771号