递归:回溯,迷宫,八皇后问题

布满荆棘的人生 2023-02-27 13:58 105阅读 0赞

1,递归的基本原则

  • 执行一个方法时,就创建一个新的受保护的独立空间(JVM栈)
  • 方法的局部变量是独立的,不会相互影响
  • 方法中使用的是引用类型变量,则会基于地址共享发生改变
  • 递归必须向退出递归的条件逼近,否则就是无限递归,最终栈溢出StackOverflowError
  • 当一个方法执行完毕,或者遇到return,就会返回,遵守谁调用,就将结果返回给谁,同时当结果返回时,该方法也执行完毕

2,迷宫问题

  • 迷宫问题注意,当前路径不是最短路径

    package com.self.datastructure.recursion;

    / 递归_迷宫问题处理 1, 迷宫二维地图初始化状态为0, 1表示墙壁, 2表示已经走过的路, 3表示死路 2, 约定迷宫走路策略: 下 -> 右 -> 上 -> 左, 逆时针尝试走 @author LiYanBin * @create 2020-01-13 16:46 /
    public class MazeDemo {

    1. public static void main(String[] args) {
    2. // 初始化迷宫, 初始化二维地图
    3. int[][] map = initMap(8, 8);
    4. // 初始化地图
    5. showDetails(map);
    6. // 从指定坐标开始找路, 抵达目的地
    7. boolean hasWay = runWay(map, 1, 1, 6, 6);
    8. showDetails(map);
    9. }
    10. /** * 迷宫走路 * @param map 地图 * @param currFirstIndex 当前位置的一维坐标 * @param currSecondIndex 当前位置的二维坐标 * @param targetFirstIndex 目标位置的一维坐标 * @param targetSecondIndex 目标位置的二维坐标 * @return */
    11. private static boolean runWay(int[][] map, int currFirstIndex, int currSecondIndex,
    12. int targetFirstIndex, int targetSecondIndex) {
    13. System.out.println("打印当前地图");
    14. showDetails(map);
    15. // 0表示初始化地图, 即未走
    16. // 1表示墙壁或者障碍, 走不通
    17. // 2表示已走
    18. // 3表示死路
    19. // 目标节点以走到, 则返回true, 并顺序返回
    20. if (currFirstIndex == targetFirstIndex && currSecondIndex == targetSecondIndex) {
    21. map[targetFirstIndex][targetSecondIndex] = 2;
    22. return true;
    23. } else { // 当前节点不是目标节点, 则继续迷宫探路
    24. // 为0表示未走过, 为2表示回溯
    25. if (map[currFirstIndex][currSecondIndex] == 0 || map[currFirstIndex][currSecondIndex] == 2) {
    26. // 修改为2, 表示已经走过
    27. map[currFirstIndex][currSecondIndex] = 2;
    28. // 先进行未知区域探索
    29. // 向下
    30. if (runUnkownArea(map, currFirstIndex + 1, currSecondIndex, targetFirstIndex, targetSecondIndex))
    31. return true;
    32. // 向右
    33. if (runUnkownArea(map, currFirstIndex, currSecondIndex + 1, targetFirstIndex, targetSecondIndex))
    34. return true;
    35. // 向上
    36. if (runUnkownArea(map, currFirstIndex - 1, currSecondIndex, targetFirstIndex, targetSecondIndex))
    37. return true;
    38. // 向下
    39. if (runUnkownArea(map, currFirstIndex, currSecondIndex - 1, targetFirstIndex, targetSecondIndex))
    40. return true;
    41. // 未知区域探索完成, 无路可走, 当前节点置位无效
    42. map[currFirstIndex][currSecondIndex] = 3;
    43. // 进行节点回溯, 继续探索
    44. // 向下
    45. if (runkownArea(map, currFirstIndex + 1, currSecondIndex, targetFirstIndex, targetSecondIndex))
    46. return true;
    47. // 向右
    48. if (runkownArea(map, currFirstIndex, currSecondIndex + 1, targetFirstIndex, targetSecondIndex))
    49. return true;
    50. // 向上
    51. if (runkownArea(map, currFirstIndex - 1, currSecondIndex, targetFirstIndex, targetSecondIndex))
    52. return true;
    53. // 向下
    54. if (runkownArea(map, currFirstIndex, currSecondIndex - 1, targetFirstIndex, targetSecondIndex))
    55. return true;
    56. } else { // 不为0或者2, 表示是墙壁或者死路
    57. return false;
    58. }
    59. }
    60. return false;
    61. }
    62. // 区域回溯
    63. private static boolean runkownArea(int[][] map, int nextFirstIndex, int nextSecondIndex, int targetFirstIndex, int targetSecondIndex) {
    64. if (map[nextFirstIndex][nextSecondIndex] != 3
    65. && runWay(map, nextFirstIndex, nextSecondIndex, targetFirstIndex, targetSecondIndex)) {
    66. return true;
    67. }
    68. return false;
    69. }
    70. // 探索未知区域
    71. private static boolean runUnkownArea(int[][] map, int nextFirstIndex, int nextSecondIndex, int targetFirstIndex, int targetSecondIndex) {
    72. if (map[nextFirstIndex][nextSecondIndex] == 0
    73. && runWay(map, nextFirstIndex, nextSecondIndex, targetFirstIndex, targetSecondIndex)) {
    74. return true;
    75. }
    76. return false;
    77. }
    78. /** * 初始化地图 * @param firstIndexCount 一位长度 * @param secondIndexCount 二维长度 * @return */
    79. private static int[][] initMap(int firstIndexCount, int secondIndexCount) {
    80. // 初始化地图, 此时地图全部状态为0
    81. int[][] map = new int[firstIndexCount][secondIndexCount];
    82. // 为地图设置围墙,
    83. // 设置上下,
    84. // [firstIndexCount - 1, 0], [0, secondIndexCount - 1]
    85. // [firstIndexCount - 1, secondIndexCount - 1]
    86. for (int i = 0; i < firstIndexCount; i++) {
    87. map[i][0] = 1;
    88. map[i][secondIndexCount - 1] = 1;
    89. }
    90. // 设置左右,
    91. // [0, 0] -> [0, secondIndexCount - 1],
    92. // [firstIndexCount - 1, 0] -> [firstIndexCount - 1, secondIndexCount - 1]
    93. for (int i = 0; i < secondIndexCount; i++) {
    94. map[0][i] = 1;
    95. map[firstIndexCount - 1][i] = 1;
    96. }
    97. // 设置障碍
    98. map[3][1] = 1;
    99. map[3][2] = 1;
    100. map[2][2] = 1;
    101. map[4][4] = 1;
    102. map[5][4] = 1;
    103. map[6][4] = 1;
    104. return map;
    105. }
    106. public static void showDetails(int[][] map) {
    107. for (int[] firstIndexData : map) {
    108. for (int secondIndexData : firstIndexData) {
    109. System.out.print(secondIndexData + " ");
    110. }
    111. System.out.println();
    112. }
    113. }

    }

3,八皇后问题

3.1,问题概述

  • 在8*8的二维桌面上,每一行摆放一枚棋子作为一个皇后
  • 任意两个皇后不能处于同一行,同一列或者同一斜线上

3.2,思路分析

  • 第一个皇后放第一行第一列
  • 第二个皇后放第二行第一列,然后判断该位置是否OK,否则继续放在第二列,第三列…,依次往后放,找到一个合适的位置
  • 继续第三个皇后,依次类推,最终找到一个合适解
  • 当得到一个正确解时,在栈回退到上一个栈时,就会开始回溯,即将第一个皇后,放在第一列的所有正确解,全部得到
  • 之后继续将第一个皇后放到第二列,继续全步骤循环,理论上会有92种结果
  • 皇后位置理论上应该用二维数组来表示,但是可以使用一维数组来实现,通过索引表示一维行号,索引上的值表示二维位置,来实现二维位置

3.3,代码实现

  1. package com.self.datastructure.recursion;
  2. /** * 递归_八皇后问题处理 * @author LiYanBin * @create 2020-01-14 14:40 **/
  3. public class QueueDemo {
  4. private final static int MAX_COUNT = 8;
  5. private final static int[] QUEUE_SORT = new int[MAX_COUNT];
  6. public static void main(String[] args) {
  7. run(0); // 从0开始摆放, 总共有92中结果
  8. }
  9. /** * 进行八皇后递归运算 * @param n 行号 */
  10. public static void run(int n) {
  11. // 序列从0开始, n为最大值说明皇后已经位置摆放完毕, 直接输出
  12. if (n == MAX_COUNT) {
  13. showDetails();
  14. return;
  15. }
  16. // 此处n表示行号, 因为摆放棋盘是 MAX_COUNT * MAX_COUNT 的二维棋盘
  17. // n表示行号, 即从该行开始摆放
  18. for (int i = 0; i < MAX_COUNT; i++) {
  19. // 此处表示把皇后摆在第 n 行的 第 i 个索引上
  20. // 初始化时候, 第一个皇后摆在[0, 0]的位置,
  21. QUEUE_SORT[n] = i;
  22. // 摆放完成后, 判断该位置是否与其他位置冲突
  23. if (judge(n)) { // 为true表示不冲突
  24. // 不冲突时, 继续进行下一个皇后摆放
  25. run(n + 1);
  26. }
  27. // 冲突, 把当前皇后位置后移一位, 通过i++表示
  28. }
  29. }
  30. // 判断当前摆放位置是否合适
  31. // 不能在同一行, 一维数组表示, 这部分不存在
  32. // 不能在同一列, 即数组中不能有重复的值
  33. // 不能在同一斜线, 即 -> |索引 - 索引| != |索引值 - 索引值|
  34. public static boolean judge(int n) {
  35. // 遍历数组, 只遍历当前数组存在元素的部分
  36. // 即 n 以前的元素
  37. for (int i = 0; i < n; i++) {
  38. // QUEUE_SORT[i] == QUEUE_SORT[n] 表示在同一列
  39. // Math.abs(i - n) == Math.abs(QUEUE_SORT[i] - QUEUE_SORT[n]) 表示在同一斜线
  40. // 斜线算法可以根据正方形对角线考虑
  41. if (QUEUE_SORT[i] == QUEUE_SORT[n] || Math.abs(i - n) == Math.abs(QUEUE_SORT[i] - QUEUE_SORT[n])) {
  42. return false;
  43. }
  44. }
  45. return true;
  46. }
  47. // 打印组合方式
  48. public static void showDetails() {
  49. for (int data : QUEUE_SORT) {
  50. System.out.print(data + " ");
  51. }
  52. System.out.println();
  53. }
  54. }

发表评论

表情:
评论列表 (有 0 条评论,105人围观)

还没有评论,来说两句吧...

相关阅读

    相关 解决皇后回溯算法

    问题简述:八皇后问题是一个古来而著名的问题,该问题是19世纪著名的数学家高斯同学提出来的。在8\8的国际象棋上摆放八个皇后,使其不能互相的攻击,也就是说,任意的两个皇后不能放在