回溯是一种渐进式寻找并构建问题解决方式的策略。我们从一个可能的动作开始并试着用这个动作解决问题。如果不能解决,就回溯并选择另-个动作直到将问题解决。根据这种行为,回溯算法会尝试所有可能的动作(如果更快找到了解决办法就尝试较少的次数)来解决问题。
有一些可用回溯解决的著名问题:
- 骑士巡逻问题.
- N皇后问题
- 迷宫老鼠问题
- 数独解题器
骑士周游问题
console.log(run(3, 4, 0, 0))
// X、Y表示棋盘X轴、Y轴大小 ,x、y 表示马儿起始位置所在X、Y轴的坐标
function run (X, Y, x, y) {
// 标记马儿所有走的路径
const visited = Array.from({length: X * Y}, () => false)
let finished = false // 标记马儿是否走完所有方格
const chessBoard = new Array(Y) // 创建一个棋盘
for(let i = 0; i < chessBoard.length; i++){
chessBoard[i] = Array.from({length: X}, () => 0)
}
return traversalChessBoard(chessBoard, x, y)
// chessBoard [][] 棋盘 x, y起始位置, step走了多少步,step等于棋盘大小时即成功走完所有棋盘
function traversalChessBoard(chessBoard, x, y, step = 1) {
chessBoard[x][y] = step // 标记棋盘走了一步
visited[x * X + y] = true
const nextSteps = next({y, x}, X, Y) // 下一步马儿能走哪些点
// 这里体现了贪心算法
nextSteps.sort((p1, p2) => next(p1, X, Y).length - next(p2, X, Y).length)
while (nextSteps.length) {
const p = nextSteps.shift()
if(!visited[p.x * X + p.y]) {
traversalChessBoard(chessBoard, p.x, p.y, step + 1)
}
}
if (step < X * Y) {
if (!finished) { // 只能走这么多步,没能走完,只好回退了, 这里体现了回溯算法
chessBoard[x][y] = 0
visited[x * X + y] = false
}
} else {
finished = true
}
return chessBoard
}
}
function next(currPoint, X, Y) {
const nextSteps = []
let y, x
// 0
if ((y = currPoint.y + 2) < X && (x = currPoint.x - 1) >= 0 ) {
nextSteps.push({y, x})
}
// 1
if ((y = currPoint.y + 2) < X && (x = currPoint.x + 1) < Y) {
nextSteps.push({y, x})
}
// 2
if ((y = currPoint.y + 1) < X && (x = currPoint.x + 2) < Y ) {
nextSteps.push({y, x})
}
// 3
if ((y = currPoint.y - 1) >= 0 && (x = currPoint.x + 2) < Y ) {
nextSteps.push({y, x})
}
// 4
if ((y = currPoint.y - 2) >= 0 && (x = currPoint.x + 1) < Y ) {
nextSteps.push({y, x})
}
// 5
if ((y = currPoint.y - 2) >= 0 && (x = currPoint.x - 1) >= 0 ) {
nextSteps.push({y, x})
}
// 6
if ((y = currPoint.y - 1) >= 0 && (x = currPoint.x - 2) >= 0 ) {
nextSteps.push({y, x})
}
// 7
if ((y = currPoint.y + 1) < X && (x = currPoint.x - 2) >= 0 ) {
nextSteps.push({y, x})
}
return nextSteps
}
老鼠迷宫
只能走简单的迷宫
console.log(ratInAMaze([
[1, 0, 0, 0],
[1, 1, 1, 1],
[0, 0, 1, 0],
[0, 1, 1, 1]
]))
function ratInAMaze(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 'NO PATH FOUND';
}
function findPath(maze, x, y, solution) {
const n = maze.length;
if (x === n - 1 && y === n - 1) {
solution[x][y] = 1;
return true;
}
if (isSafe(maze, x, y) === true) {
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;
if (x >= 0 && y >= 0 && x < n && y < n && maze[x][y] !== 0) {
return true;
}
return false;
}
复原ip
给定一个只包含数字的字符串,复原它并返回所有可能的 IP 地址格式。
有效的 IP 地址正好由四个整数(每个整数位于 0 到 255 之间组成),整数之间用 ‘.’ 分隔。
function run (str) {
const list = []
figureItOut(str, [])
return list
function figureItOut (str, arr) {
if (!str.length && arr.length == 4) {
list.push(arr.join('.'))
return
}
if (arr.length == 4 ) {
return
}
let tp = '', tps = str.split('')
for (let i = 1; i <= 3; i++) {
tp = tps.pop() + tp
if (legal(tp)) {
let tpa = arr.slice()
tpa.unshift(tp)
figureItOut(tps.join(''), tpa)
}
}
}
function legal(num) {
if (Number(num) < 256 && Number(num).toString() == num) {
return true
}
return false
}
}
console.log(run('25525511135'))