说明
-
主函数 solveSudoku:
- 调用 backtrack 函数尝试解决数独问题。
-
回溯函数 backtrack:
- 遍历数独中的每个单元格。
- 如果单元格为空(
'.'
),尝试填充数字 1-9。 - 检查是否可以放置数字,如果可以,放置后继续递归解决。
- 如果递归返回
false
,撤销当前数字,回溯尝试下一个数字。
-
验证函数 boxIndex:
- 检查当前数字是否在同一行、同一列或同一 3x3 宫格中已经存在。
复杂度分析
-
时间复杂度:
- 在最坏情况下(填充的空格很多),复杂度接近 O(9m)O(9^{m})O(9m),其中 mmm 是空格的数量。
- 辅助数组显著降低了检查的开销,实际效率更高。
-
空间复杂度:
- 辅助数组的空间复杂度为 O(9×9)O(9 \times 9)O(9×9),递归栈的深度最大为 O(81)。
function solveSudoku(&$board) {
// 定义辅助数组,记录每行、每列、每个 3x3 宫格是否已经存在某个数字
$rows = array_fill(0, 9, array_fill(0, 9, false));
$cols = array_fill(0, 9, array_fill(0, 9, false));
$boxes = array_fill(0, 9, array_fill(0, 9, false));
// 初始化辅助数组
for ($i = 0; $i < 9; $i++) {
for ($j = 0; $j < 9; $j++) {
if ($board[$i][$j] !== '.') {
$num = intval($board[$i][$j]) - 1; // 转换为索引
$rows[$i][$num] = true;
$cols[$j][$num] = true;
$boxes[$this->boxIndex($i, $j)][$num] = true;
}
}
}
// 开始填充数独
$this->backtrack($board, 0, $rows, $cols, $boxes);
}
// 辅助函数:回溯填充
function backtrack(&$board, $pos, &$rows, &$cols, &$boxes) {
// 找到下一个需要填充的空格
for ($i = $pos; $i < 81; $i++) {
$row = intdiv($i, 9);
$col = $i % 9;
if ($board[$row][$col] === '.') {
// 尝试填充 1-9
for ($num = 0; $num < 9; $num++) {
$boxIdx = $this->boxIndex($row, $col);
if (!$rows[$row][$num] && !$cols[$col][$num] && !$boxes[$boxIdx][$num]) {
// 更新状态
$board[$row][$col] = strval($num + 1);
$rows[$row][$num] = $cols[$col][$num] = $boxes[$boxIdx][$num] = true;
// 递归填充下一格
if ($this->backtrack($board, $i + 1, $rows, $cols, $boxes)) {
return true;
}
// 回溯
$board[$row][$col] = '.';
$rows[$row][$num] = $cols[$col][$num] = $boxes[$boxIdx][$num] = false;
}
}
return false; // 如果当前格无法填充,返回 false
}
}
return true; // 填充完成
}
// 辅助函数:计算当前格的 3x3 宫格索引
function boxIndex($row, $col) {
return intdiv($row, 3) * 3 + intdiv($col, 3);
}