编写一个程序,通过填充空格来解决数独问题。
数独的解法需 遵循如下规则:
数独部分空格内已填入了数字,空白格用 ‘.’ 表示。
困难
点击在LeetCode中查看题目
输入: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"]]
board.length == 9
board[i].length == 9
board[i][j]
是一个数字或者 '.'
这是一个经典的回溯算法问题,我们需要尝试填充每个空格,并在发现错误时及时回溯。
关键点:
具体步骤:
时间复杂度:O(9^m),m为空格数量
空间复杂度:O(m),递归栈的深度
步骤 | 当前位置 | 尝试数字 | 结果 | 说明 |
---|---|---|---|---|
初始状态 | (0,2) | 1-9 | 4可用 | 第一个空格位置 |
继续填充 | (0,3) | 1-9 | 6可用 | 成功填入4后继续 |
回溯 | (2,0) | 1-9 | 1可用 | 前面填充正确,继续 |
完成 | (8,8) | - | 成功 | 所有空格已填满 |
数据结构 | 用途 | 实现方式 | 优势 |
---|---|---|---|
行状态 | 记录每行数字 | 位运算 | 快速查找和更新 |
列状态 | 记录每列数字 | 位运算 | 空间效率高 |
宫状态 | 记录3x3宫数字 | 位运算 | 验证速度快 |
操作 | 二进制表示 | 说明 |
---|---|---|
添加数字5 | 00010000 | 第5位置1 |
删除数字5 | 11101111 | 第5位置0 |
检查数字5 | &00010000 | 与运算判断 |
public class Solution {
private int[] rows = new int[9];
private int[] cols = new int[9];
private int[] boxes = new int[9];
private bool solved = false;
public void SolveSudoku(char[][] board) {
// 初始化状态
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
if (board[i][j] != '.') {
int num = board[i][j] - '0';
int pos = 1 << (num - 1);
rows[i] |= pos;
cols[j] |= pos;
boxes[(i/3)*3 + j/3] |= pos;
}
}
}
// 开始回溯
Backtrack(board, 0, 0);
}
private bool Backtrack(char[][] board, int row, int col) {
if (col == 9) {
row++;
col = 0;
}
if (row == 9) return true;
if (board[row][col] != '.') {
return Backtrack(board, row, col + 1);
}
int boxIndex = (row/3)*3 + col/3;
for (int num = 1; num <= 9; num++) {
int pos = 1 << (num - 1);
if ((rows[row] & pos) == 0 &&
(cols[col] & pos) == 0 &&
(boxes[boxIndex] & pos) == 0) {
board[row][col] = (char)(num + '0');
rows[row] |= pos;
cols[col] |= pos;
boxes[boxIndex] |= pos;
if (Backtrack(board, row, col + 1)) {
return true;
}
board[row][col] = '.';
rows[row] &= ~pos;
cols[col] &= ~pos;
boxes[boxIndex] &= ~pos;
}
}
return false;
}
}
解法 | 时间复杂度 | 空间复杂度 | 优点 | 缺点 |
---|---|---|---|---|
暴力回溯 | O(9^m) | O(m) | 实现简单 | 效率低 |
位运算优化 | O(9^m) | O(1) | 性能好 | 代码复杂 |
Dancing Links | O(9^m) | O(n^2) | 最优解法 | 实现难度大 |