0. 概念
一种渐进式的寻找并构建问题及解决方案的策略。从第一个动作出发并使用这个动作解决问题,如果问题不能解决,就回溯,使用下一个动作尝试解决,依次类推。
1. 迷宫老鼠问题
- 迷宫(maze)用一个二维矩阵表示,如果元素值为1,表示位置空闲,如果元素值为0,表示位置被占用。
- 创建一个维度和迷宫矩阵相同的二维数组(solution),所有元素值为0,该矩阵用来记录老鼠走过的路径,老鼠走过的元素值为1。
- 老鼠从0,0位置要到达位置,x,y,尝试往下走,走过的位置标注为1,如果这条路能够到达终点,solution中记录的路径就是老鼠走过的路径;如果这条路不能到达终点,要逐级返回到出发点,并将在solution中标记的路径清除。
function rateInAMaze(maze) {
const solution = [];
// 创建保存老鼠路径的二维数组
for (let i = 0; i < maze.length; i++) {
solution[i] = [];
for (let j = 0; j < maze[i].length; j++) {
solution[i][j] = 0;
}
}
if (findPath(maze, 0, 0, solution) === true) {
return solution;
}
return -1;
}
function findPath(maze, x, y, solution) {
const n = maze.length;
const m = maze[0].length;
// 到达终点
if (x === n - 1 && y === m - 1) {
solution[x][y] = 1;
return true;
}
if (isSafe(maze, x, y)) {
solution[x][y] = 1;
// 判断下一个垂直位置能否移动
if (findPath(maze, x + 1, y, solution)) {
return true;
}
// 判断下一个水平位置能否移动
if (findPath(maze, x, y + 1, solution)) {
return true;
}
solution[x][y] = 0;
return false;
}
return false;
}
// 判断位置是否为空闲
function isSafe(maze, x, y) {
const n = maze.length;
const m = maze[0].length;
return x >=0 && y >= 0 && x < n && y < m && maze[x][y] !== 0;
}
const maze = [
[1, 0, 0, 0],
[1, 1, 1, 1],
[0, 0, 1, 0],
[0, 1, 1, 1]
]
console.log('rateInAMaze:', rateInAMaze(maze));
2. 数独解题器
数独是一个非常有趣的解谜游戏,也是史上最流行的游戏之一。目标是用数字1~9填满一个9×9的矩阵,要求每行和每列都由这九个数字构成。矩阵还包含了小方块(3×3矩阵),它们同样需要分别用这九个数字填满。
每个位置数字的特点:这个数字在所在行、所在列表、所在3X3矩阵都没有重复出现过。
- 判断某个位置是不是空的,空的位置元素值为0;
- 如果是空的,将1~9范围内的每个数字,依次放入该位置,每次放入一个数字,接下来验证下一个位置,依次类推如果能形成正确的解决方案,证明这个数字放在这个位置没有问题,如果没有形成正确的方案,表示这个数字不应该在这个位置,将这个位置重新设置成0。将下一个数字放入该位置,继续向下验证。
- 如果不是空的,继续判断下一个位置是不是空,重复1,2。
// 主函数
function sudokuSolver(matrix) {
if (solverSudoku(matrix) === true) {
return matrix;
}
return -1;
}
const UNASSIGNED = 0;
function solverSudoku(matrix) {
let checkBlankSpace = false;
let row = 0;
let col = 0;
for (row = 0; row < matrix.length; row++) {
for (col = 0; col < matrix[row].length; col++) {
if (matrix[row][col] === UNASSIGNED) {
checkBlankSpace = true;
break;
}
}
if (checkBlankSpace === true) {
break;
}
}
// 所有位置都不是空,表示问题已解决
if (checkBlankSpace === false) {
return true;
}
// 处理空格
for (let num = 1; num <= 9; num++) {
if (isSafe(matrix, row, col, num)) {
matrix[row][col] = num;
if (solverSudoku(matrix)) {
return true;
}
matrix[row][col] = UNASSIGNED;
}
}
return false;
}
function isSafe(matrix, row, col, num) {
return (!useInRow(matrix, row, num) &&
!useInCol(matrix, col, num) &&
!useInSubMatrix(matrix, row - (row % 3), col - (col % 3), num));
}
function useInRow(matrix, row, num) {
for (let i = 0; i < matrix[row].length; i++) {
if (matrix[row][i] === num) return true;
}
return false;
}
function useInCol(matrix, col, num) {
for (let i = 0; i < matrix.length; i++) {
if (matrix[i][col] === num) return true;
}
return false;
}
function useInSubMatrix(matrix, leftStartRow, leftStartCol, num) {
for (let row = 0; row < 3; row++) {
for (let col = 0; col < 3; col++) {
if (matrix[row + leftStartRow][col + leftStartCol] === num) {
return true;
}
}
}
return false;
}
const sudokuGrid = [
[5, 3, 0, 0, 7, 0, 0, 0, 0],
[6, 0, 0, 1, 9, 5, 0, 0, 0],
[0, 9, 8, 0, 0, 0, 0, 6, 0],
[8, 0, 0, 0, 6, 0, 0, 0, 3],
[4, 0, 0, 8, 0, 3, 0, 0, 1],
[7, 0, 0, 0, 2, 0, 0, 0, 6],
[0, 6, 0, 0, 0, 0, 2, 8, 0],
[0, 0, 0, 4, 1, 9, 0, 0, 5],
[0, 0, 0, 0, 8, 0, 0, 7, 9]
];
console.log(sudokuSolver(sudokuGrid));