1.递归应用场景


看个实际应用场景,迷宫问题(回溯), 递归(Recursion)

2.递归的概念


简单的说:递归就是方法自己调用自己,每次调用时传入不同的变量.递归有助于编程者解决复杂的问题,同时
可以让代码变得简洁。

3.递归调用机制


我列举两个小案例,来帮助大家理解递归,部分学员已经学习过递归了,这里在给大家回顾一下递归调用机制
1)打印问题
2)阶乘问题
3)使用图解方式说明了递归的调用机制
image.png
4)代码

  1. public class RecursionTest {
  2. public static void main(String[] args) {
  3. test(5);
  4. System.err.println(factorial(6));
  5. }
  6. public static void test(int n) {
  7. if (n > 2) {
  8. test(n - 1);
  9. } //else {
  10. System.out.println("n=" + n);
  11. // }
  12. }
  13. //阶乘问题
  14. public static int factorial(int n) {
  15. if (n == 1) {
  16. return 1;
  17. } else {
  18. return factorial(n - 1) * n; // 1 * 2 * 3
  19. }
  20. }
  21. }

image.png

4.递归能解决什么样的问题

1)各种数学问题如: 8皇后问题,汉诺塔,阶乘问题,迷宫问题,球和篮子的问题(google编程大赛)
2)各种算法中也会使用到递归,比如快排,归并排序,二分查找,分治算法等.
3)将用栈解决的问题—>第归代码比较简

5.递归需要遵守的重要规则

1)执行一个方法时,就创建一个新的受保护的独立空间(栈空间)
2)方法的局部变量是独立的,不会相互影响,比如n变量
3)如果方法中使用的是引用类型变量(比如数组),就会共享该引用类型的数据.
4)递归必须向退出递归的条件逼近,否则就是无限递归,出现StackOverflowError,死龟了:)
5)当一个方法执行完毕,或者遇到return,就会返回,遵守谁调用,就将结果返回给谁,同时当方法执行完毕或者返回时,该方法也就执行完毕

6.迷宫

思路

  1. 使用递归回溯来给小球找路
  2. map表示地图
  3. i,j表示从地图的哪个位置开始出发(1,1)
  4. 如果小球能到map[6][5]位置,则说明通路找到.
  5. 约定: 当map[i][j]为0表示该点没有走过 当为1表示墙;2表示通路可以走 ;3表示该点已经走过,但是走不通尚在走迷宫时,需要确定一个策略(方法)下->右->上->左,如果该点走不通,再回溯

    代码

    ```java package com.sgg.datastructure.recursion;

public class 迷宫 { /**

  1. * @param i
  2. * @param j
  3. */
  4. public static int[][] init(int i, int j) {
  5. int[][] map = new int[i][j];
  6. //外面一圈是墙
  7. for (int x = 0; x < 7; x++) {
  8. map[0][x] = 1;
  9. map[7][x] = 1;
  10. }
  11. for (int y = 1; y < 7; y++) {
  12. map[y][0] = 1;
  13. map[y][6] = 1;
  14. }
  15. return map;
  16. }
  17. public static void show(int[][] map) {
  18. for (int[] ints : map) {
  19. for (int i : ints) {
  20. System.out.printf("%d\t", i);
  21. }
  22. System.out.println();
  23. }
  24. }
  25. public static void main(String[] args) {
  26. int[][] map = init(8, 7);
  27. map[3][1] = 1;
  28. map[3][2] = 1;
  29. show(map);
  30. System.out.println(setWay(map,1,1,6,5));
  31. show(map);
  32. }
  33. /**
  34. * 利用递归解决迷宫问题
  35. * 思路,0代表没走过,1代表墙,2代表走成功了,3代表不成功
  36. * 先下,然后右,然后走上,然后左
  37. * @param map
  38. * @param iy
  39. * @param ix
  40. * @param resultY
  41. * @param resultX
  42. * @return
  43. */
  44. public static boolean setWay(int[][] map, int iy, int ix, int resultY, int resultX) {
  45. if (map[resultY][resultX] == 2) {
  46. return true;
  47. }
  48. if (map[iy][ix]==0) {
  49. map[iy][ix] = 2;
  50. if (setWay(map, iy+1, ix ,resultY,resultX)) {//下
  51. return true;
  52. }else if(setWay(map, iy, ix+1 ,resultY,resultX)){//右
  53. return true;
  54. } else if (setWay(map, iy-1, ix, resultY, resultX)) {//上
  55. return true;
  56. }else if (setWay(map, iy, ix-1, resultY, resultX)) {//左
  57. return true;
  58. }else {
  59. map[iy][ix] = 3;
  60. return false;
  61. }
  62. }else {
  63. return false;
  64. }
  65. }

}

  1. <a name="CNqlg"></a>
  2. ## 作业
  3. 求最短的路径
  4. <a name="pSkdw"></a>
  5. ### 思路
  6. 用不同的策略,然后比较需要走的步数
  7. <a name="LjpIU"></a>
  8. ### 代码
  9. 以后做<br />![03.jpeg](https://cdn.nlark.com/yuque/0/2021/jpeg/655064/1640055017527-f5bfb798-1f6c-44a4-ae25-eae6f55cff3c.jpeg#clientId=u902c52c0-b80c-4&crop=0&crop=0&crop=1&crop=1&from=ui&id=u5f9da45f&margin=%5Bobject%20Object%5D&name=03.jpeg&originHeight=150&originWidth=150&originalType=binary&ratio=1&rotation=0&showTitle=false&size=8284&status=done&style=none&taskId=u7f21b5e3-2ea2-4cce-a414-5a795106d93&title=)
  10. <a name="c8HnQ"></a>
  11. # 7.八皇后
  12. <a name="mWtuR"></a>
  13. ## 介绍
  14. 八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即:任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法(92)
  15. <a name="Ugzow"></a>
  16. ## 思路
  17. 1. 第一个皇后先放第一行第一列
  18. 1. 第二个皇后放在第二行第一列、然后判断是否OK, 如果不OK,继续放在第二列、第三列、依次把所有列都<br />放完,找到一个合适
  19. 1. 继续第三个皇后,还是第一列、第二列......直到第8个皇后也能放在一个不冲突的位置,算是找到了一个正确<br />
  20. 1. 当得到一个正确解时,在栈回退到上一个栈时,就会开始回溯,即将第一个皇后,放到第一列的所有正确解,<br />全部得到.
  21. 1. 然后回头继续第一个皇后放第二列,后面继续循环执行1,2,3,4的步骤
  22. 1. 示意图:
  23. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/655064/1640055178385-dd47c8e6-e321-4fa3-9404-c5f80b4f7bcf.png#clientId=u902c52c0-b80c-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=301&id=u14ecbf80&margin=%5Bobject%20Object%5D&name=image.png&originHeight=401&originWidth=664&originalType=binary&ratio=1&rotation=0&showTitle=false&size=236418&status=done&style=none&taskId=ubd1eaa08-e0be-4a23-adb0-535f79a4f29&title=&width=498)
  24. <a name="zs0ME"></a>
  25. ## 说明
  26. 理论上应该创建一个二维数组来表示棋盘,但是实际上可以通过算法,<br />用一个**一维数组**即可解决问题. arr[8] ={0 , 4, 7, 5, 2, 6, 1, 3} <br />对应arr下标 表示第几行,即第几个皇后,arr[i] = val , val表示第i+1个皇后,放在第i+1行的第val+1列
  27. <a name="FWH4h"></a>
  28. ## 代码
  29. ```java
  30. package com.sgg.datastructure.recursion;
  31. import java.util.Arrays;
  32. public class Queue8 {
  33. int max = 8;//代表几个皇后,也代表棋盘的大小
  34. int[] array = new int[max];//保存皇后放置位置的结果,比如 arr = {0 , 4, 7, 5, 2, 6, 1, 3}
  35. static int count = 0;//有几种可能
  36. /**
  37. * 放置第n个皇后
  38. */
  39. public void check(int n) {
  40. if (n == max) {//跳出的条件
  41. print();
  42. return;
  43. }
  44. for (int i = 0; i < max; i++) {
  45. array[n] = i;
  46. if (judge(n)) {
  47. check(n + 1);
  48. }
  49. }
  50. }
  51. /**
  52. * 判断放置了这个皇后,跟之前的是否冲突
  53. */
  54. private boolean judge(int n) {
  55. for (int i = 0; i < n; i++) {
  56. if (array[i] == array[n]) {
  57. return false;
  58. }
  59. if (Math.abs(n - i) == Math.abs(array[n] - array[i])) {
  60. return false;
  61. }
  62. }
  63. return true;
  64. }
  65. private void print() {
  66. count++;
  67. Arrays.stream(array).forEach(x -> System.out.print(x + " "));
  68. System.out.println();
  69. }
  70. public static void main(String[] args) {
  71. Queue8 queue8 = new Queue8();
  72. queue8.check(0);
  73. System.out.printf("一共有%d 解法", count);
  74. }
  75. }