回溯算法是解数独最自然、最通用的方法,核心为“试错+撤回”:找首个空格填1–9并三重验证(行、列、3×3宫格),递归求解,失败则撤回;可优化为预记录行列宫布尔数组及早终止。

回溯算法是解数独最自然、最通用的方法,核心在于“试错+撤回”:对每个空格依次尝试 1–9,填入后验证是否合规;若后续无解,则清空当前格,换下一个数字继续试。整个过程用递归实现,简洁清晰,适合初学者掌握算法思想。
找准空格再动手
不是按顺序硬扫,而是先遍历整个 9×9 网格,找到第一个值为 '.'(或 0,取决于输入格式)的位置。一旦定位,就专注处理它——填数、验证、递归下一位。如果当前格已有数字,直接跳到下一个位置,避免无效操作。
- 常用写法是双层 for 循环嵌套,从 (0,0) 开始逐行扫描
- 也可封装成辅助方法 findNextEmpty(),返回 int[2] 表示 [row, col]
- 遇到边界(如第 0 行第 8 列),下一位是 (1,0),注意行进逻辑别越界
填数前必须做三重验证
一个数字能否填进 board[i][j],要同时满足三个条件:
- 行不重复:检查 board[i][0] 到 board[i][8] 中是否已存在该数字
- 列不重复:检查 board[0][j] 到 board[8][j] 中是否已存在该数字
- 3×3 宫格不重复:计算宫格左上角坐标 (i/3*3, j/3*3),再遍历其 3×3 范围内所有格子
验证函数通常命名为 isValid(board, i, j, num),返回 boolean。建议单独抽离,便于复用和调试。
立即学习“Java免费学习笔记(深入)”;
递归调用与回溯撤回要配对
每次成功填入一个合法数字后,立刻递归求解下一个空格。若递归返回 true,说明整条路径走通,直接向上返回;若返回 false,说明这条路走不通,必须把当前格子还原为 '.',让外层循环尝试下一个数字。
- 关键语句:board[i][j] = num; → if (backtrack()) return true; → board[i][j] = '.';
- 千万不能漏掉最后一步“撤回”,否则会导致污染后续搜索
- 当所有空格填完(即遍历结束未发现 '.'),递归自然返回 true,表示解已找到
加点小优化更实用
标准回溯能解,但加一点策略可明显提速,尤其对难题:
- 用三个布尔数组预记录:rows[9][10]、cols[9][10]、blocks[3][3][10],分别标记某行/列/宫是否已用过数字 1–9,查表 O(1),比每次遍历快得多
- 优先选“候选数最少”的空格(MRV 启发式),减少分支数;不过对 9×9 数独,简单顺序扫描已足够
- 设全局布尔变量 solved,一找到解就终止全部递归,避免多余搜索



















