JavaScript数独求解递归后回退至初始网格无法运行问题排查
问题根因
你的回溯逻辑缺少解命中后的终止传递机制:
- 当递归走到无剩余空格的节点(即找到合法数独解)时,当前函数仅执行了网格打印,没有向上层递归返回“已找到最终解”的标记
- 所有上层递归调用
solve()后,默认收到undefined假值,会无条件执行grid[y][x] = 0的回溯擦除逻辑,从递归最深处一路回退,最终把所有填入的数字重置为0,回到初始网格状态 - 这个问题和JavaScript、Python的语言差异无关,你之前能跑通的Python版本一定是加了对应的终止判断,阻断了找到解之后的回溯流程。
修复方案
给solve函数增加布尔返回值作为递归状态标记:
- 遍历完所有9*9单元格、没有剩余待填空格时,说明找到合法解,返回
true - 递归调用
solve()时如果收到true返回值,说明下层已经找到最终解,直接向上层返回true,跳过后续的回溯擦除步骤 - 当前空格尝试完1-9所有数字都不合法时,返回
false,正常触发回溯
修复后的可运行代码:
let grid = [[2, 5, 0, 0, 3, 0, 9, 0, 1], [0, 1, 0, 0, 0, 4, 0, 0, 0], [4, 0, 7, 0, 0, 0, 2, 0, 8], [0, 0, 5, 2, 0, 0, 0, 0, 0], [0, 0, 0, 0, 9, 8, 1, 0, 0], [0, 4, 0, 0, 0, 3, 0, 0, 0], [0, 0, 0, 3, 6, 0, 0, 7, 2], [0, 7, 0, 0, 0, 0, 0, 0, 3], [9, 0, 3, 0, 0, 0, 6, 0, 4]] function possible(y, x, n){ for (let i = 0; i < 9; i++){ if (grid[i][x] === n){ return false } } for (let i = 0; i < 9; i++){ if (grid[y][i] === n){ return false } } let xx = Math.floor(x / 3) * 3 let yy = Math.floor(y / 3) * 3 for (let i = 0; i < 3; i++){ for (let j = 0; j < 3; j++){ if (grid[yy + i][xx + j] === n){ return false } } } return true } function solve(){ for (let y = 0; y < 9; y++){ for (let x = 0; x < 9; x++){ if (grid[y][x] === 0){ for (let n = 1; n < 10; n++){ if (possible(y, x, n)){ grid[y][x] = n // 下层找到解则直接向上传递,不执行回溯 if (solve()) return true grid[y][x] = 0 } } // 当前格无合法数字,返回假值触发回溯 return false } } } // 所有空格填充完成,打印结果并返回真值终止递归 console.log(grid) return true } solve()
现象解释
你运行原代码时控制台能短暂打印出正确解,是因为打印动作发生在递归最深层、回溯逻辑还未执行的节点;打印完成后递归栈逐层弹出,回溯擦除逻辑就会把所有填入的数字重置,最终回到初始网格状态。
内容的提问来源于stack exchange,提问作者Shel Don
相关产品推荐
相关产品推荐

