JavaScript递归生成数独终盘 函数中途停止无报错问题排查
问题根因
你的代码故障和JavaScript递归机制无关,是两处基础的语法使用和变量作用域错误导致回溯逻辑完全失效:
- 第一处致命错误:你用
Array.forEach遍历候选数字列表,但forEach本身不支持中途中断循环,回调函数里写的return true只会跳过当前遍历项,既不会终止整个遍历流程,也不会让forEach本身返回布尔值给外层判断。当递归走到死路需要回退换数时,代码根本不会正确执行回溯,走到走不通的节点就直接停了。 - 第二处错误:
row和col被定义为全局变量,递归调用下层fillGrid的时候,这两个变量的值会被下层循环覆盖,等递归回溯回到上层时,row/col已经不是当前层正在处理的单元格坐标,最后执行grid[row][col] = 0回溯重置的时候,会把之前正确填好的其他单元格清成0,直接打乱整个网格状态。
修复方案
- 把
forEach遍历换成普通for...of循环,让循环内的return可以正确终止循环、向上层传递布尔返回值,让回溯逻辑正常生效。 - 把
row、col改成fillGrid函数内部的局部变量,不要用全局声明,避免不同递归层级之间互相覆盖坐标值。 - 给
fillGrid补全明确返回值:当所有候选数字都试完仍走不通时,返回false通知上层需要回溯换数。 - 冗余的宫格判断逻辑可以简化,不用写9个分支提取宫格内容,直接通过坐标计算宫格起始位置做冲突校验即可,减少出错概率。
修复后可直接运行的完整代码
// Fisher-Yates 数组洗牌工具 function shuffle(array) { let currentIndex = array.length, randomIndex; while (currentIndex != 0) { randomIndex = Math.floor(Math.random() * currentIndex); currentIndex--; [array[currentIndex], array[randomIndex]] = [ array[randomIndex], array[currentIndex]]; } return array; } // 初始化9x9空网格 let grid = Array(9).fill().map(() => Array(9).fill(0)); const numberList = [1, 2, 3, 4, 5, 6, 7, 8, 9]; // 检查网格是否填充完成 function checkGrid() { for (let i = 0; i < 9; i++) { for (let j = 0; j < 9; j++) { if (grid[i][j] === 0) return false; } } return true; } // 核心回溯填充函数 function fillGrid() { // 坐标改为函数内局部变量,避免递归层级互相污染 let row, col; for (let i = 0; i < 81; i++) { row = Math.floor(i / 9); col = i % 9; if (grid[row][col] === 0) { shuffle(numberList); // 替换forEach为普通for循环,支持中断和返回值传递 for (const v of numberList) { // 检查行重复 if (grid[row].includes(v)) continue; // 检查列重复 let colConflict = false; for (let r = 0; r < 9; r++) { if (grid[r][col] === v) { colConflict = true; break; } } if (colConflict) continue; // 定位当前所属3x3宫格,检查宫格重复 const squareRowStart = Math.floor(row / 3) * 3; const squareColStart = Math.floor(col / 3) * 3; let squareConflict = false; for (let r = squareRowStart; r < squareRowStart + 3; r++) { for (let c = squareColStart; c < squareColStart + 3; c++) { if (grid[r][c] === v) { squareConflict = true; break; } } if (squareConflict) break; } if (squareConflict) continue; // 数值合法,填入单元格 grid[row][col] = v; if (checkGrid()) { return true; } // 递归填充下一个单元格,返回true说明找到合法解,直接向上传递 if (fillGrid()) { return true; } // 递归返回false说明当前值走不通,重置单元格回溯 grid[row][col] = 0; } // 所有候选值都试完仍无解,返回false通知上层回溯 return false; } } return true; } // 生成数独终盘并打印 fillGrid(); grid.forEach(row => console.log(row));
内容的提问来源于stack exchange,提问作者jokeriino
相关产品推荐
相关产品推荐

