回溯是一种渐进式寻找并构建问题解决方式的策略。我们从一个可能的动作开始并试着用这个动作解决问题。如果不能解决,就回溯并选择另-个动作直到将问题解决。根据这种行为,回溯算法会尝试所有可能的动作(如果更快找到了解决办法就尝试较少的次数)来解决问题。

有一些可用回溯解决的著名问题:

  • 骑士巡逻问题.
  • N皇后问题
  • 迷宫老鼠问题
  • 数独解题器

骑士周游问题

image.png
image.png

image.png

  1. console.log(run(3, 4, 0, 0))
  2. // X、Y表示棋盘X轴、Y轴大小 ,x、y 表示马儿起始位置所在X、Y轴的坐标
  3. function run (X, Y, x, y) {
  4. // 标记马儿所有走的路径
  5. const visited = Array.from({length: X * Y}, () => false)
  6. let finished = false // 标记马儿是否走完所有方格
  7. const chessBoard = new Array(Y) // 创建一个棋盘
  8. for(let i = 0; i < chessBoard.length; i++){
  9. chessBoard[i] = Array.from({length: X}, () => 0)
  10. }
  11. return traversalChessBoard(chessBoard, x, y)
  12. // chessBoard [][] 棋盘 x, y起始位置, step走了多少步,step等于棋盘大小时即成功走完所有棋盘
  13. function traversalChessBoard(chessBoard, x, y, step = 1) {
  14. chessBoard[x][y] = step // 标记棋盘走了一步
  15. visited[x * X + y] = true
  16. const nextSteps = next({y, x}, X, Y) // 下一步马儿能走哪些点
  17. // 这里体现了贪心算法
  18. nextSteps.sort((p1, p2) => next(p1, X, Y).length - next(p2, X, Y).length)
  19. while (nextSteps.length) {
  20. const p = nextSteps.shift()
  21. if(!visited[p.x * X + p.y]) {
  22. traversalChessBoard(chessBoard, p.x, p.y, step + 1)
  23. }
  24. }
  25. if (step < X * Y) {
  26. if (!finished) { // 只能走这么多步,没能走完,只好回退了, 这里体现了回溯算法
  27. chessBoard[x][y] = 0
  28. visited[x * X + y] = false
  29. }
  30. } else {
  31. finished = true
  32. }
  33. return chessBoard
  34. }
  35. }
  36. function next(currPoint, X, Y) {
  37. const nextSteps = []
  38. let y, x
  39. // 0
  40. if ((y = currPoint.y + 2) < X && (x = currPoint.x - 1) >= 0 ) {
  41. nextSteps.push({y, x})
  42. }
  43. // 1
  44. if ((y = currPoint.y + 2) < X && (x = currPoint.x + 1) < Y) {
  45. nextSteps.push({y, x})
  46. }
  47. // 2
  48. if ((y = currPoint.y + 1) < X && (x = currPoint.x + 2) < Y ) {
  49. nextSteps.push({y, x})
  50. }
  51. // 3
  52. if ((y = currPoint.y - 1) >= 0 && (x = currPoint.x + 2) < Y ) {
  53. nextSteps.push({y, x})
  54. }
  55. // 4
  56. if ((y = currPoint.y - 2) >= 0 && (x = currPoint.x + 1) < Y ) {
  57. nextSteps.push({y, x})
  58. }
  59. // 5
  60. if ((y = currPoint.y - 2) >= 0 && (x = currPoint.x - 1) >= 0 ) {
  61. nextSteps.push({y, x})
  62. }
  63. // 6
  64. if ((y = currPoint.y - 1) >= 0 && (x = currPoint.x - 2) >= 0 ) {
  65. nextSteps.push({y, x})
  66. }
  67. // 7
  68. if ((y = currPoint.y + 1) < X && (x = currPoint.x - 2) >= 0 ) {
  69. nextSteps.push({y, x})
  70. }
  71. return nextSteps
  72. }

老鼠迷宫

只能走简单的迷宫

  1. console.log(ratInAMaze([
  2. [1, 0, 0, 0],
  3. [1, 1, 1, 1],
  4. [0, 0, 1, 0],
  5. [0, 1, 1, 1]
  6. ]))
  7. function ratInAMaze(maze) {
  8. const solution = [];
  9. for (let i = 0; i < maze.length; i++) {
  10. solution[i] = [];
  11. for (let j = 0; j < maze[i].length; j++) {
  12. solution[i][j] = 0;
  13. }
  14. }
  15. if (findPath(maze, 0, 0, solution) === true) {
  16. return solution;
  17. }
  18. return 'NO PATH FOUND';
  19. }
  20. function findPath(maze, x, y, solution) {
  21. const n = maze.length;
  22. if (x === n - 1 && y === n - 1) {
  23. solution[x][y] = 1;
  24. return true;
  25. }
  26. if (isSafe(maze, x, y) === true) {
  27. solution[x][y] = 1;
  28. if (findPath(maze, x + 1, y, solution)) {
  29. return true;
  30. }
  31. if (findPath(maze, x, y + 1, solution)) {
  32. return true;
  33. }
  34. solution[x][y] = 0;
  35. return false;
  36. }
  37. return false;
  38. }
  39. function isSafe(maze, x, y) {
  40. const n = maze.length;
  41. if (x >= 0 && y >= 0 && x < n && y < n && maze[x][y] !== 0) {
  42. return true;
  43. }
  44. return false;
  45. }

复原ip

给定一个只包含数字的字符串,复原它并返回所有可能的 IP 地址格式。
有效的 IP 地址正好由四个整数(每个整数位于 0 到 255 之间组成),整数之间用 ‘.’ 分隔。

  1. function run (str) {
  2. const list = []
  3. figureItOut(str, [])
  4. return list
  5. function figureItOut (str, arr) {
  6. if (!str.length && arr.length == 4) {
  7. list.push(arr.join('.'))
  8. return
  9. }
  10. if (arr.length == 4 ) {
  11. return
  12. }
  13. let tp = '', tps = str.split('')
  14. for (let i = 1; i <= 3; i++) {
  15. tp = tps.pop() + tp
  16. if (legal(tp)) {
  17. let tpa = arr.slice()
  18. tpa.unshift(tp)
  19. figureItOut(tps.join(''), tpa)
  20. }
  21. }
  22. }
  23. function legal(num) {
  24. if (Number(num) < 256 && Number(num).toString() == num) {
  25. return true
  26. }
  27. return false
  28. }
  29. }
  30. console.log(run('25525511135'))