认识递归

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

  1. public static int factorial(int n) {
  2. if (n == 1) {
  3. return 1;
  4. } else {
  5. return factorial(n - 1) * n;
  6. }
  7. }

递归的调用机制

为了说明这一点,以递归调用f(x1)→f(x2)→f(x3)为例,展示了执行步骤的顺序和堆栈布局
image.png
为了调用f(x2),会给f(x1)分配一个栈空间。同理,在f(x2)中也会为了调用f(x3)而分配另一个栈空间。最终在f(x3)中,程序到达基本情况,因此没有在f(x3)中进一步递归


递归调用规则:
1. 当程序执行到一个方法时,就会开辟一个独立的空间(栈)
2. 每个空间的数据(局部变量),是独立的

递归需要遵守的重要规则

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

简单来说:避免出现死循环
实际生产中,也是不能出现太大数据的循环的,影响速度

实际案例

迷宫问题

image.png

进行迷宫之前,我们需要对此作出一些说明,这样更方便代码的进行
说明

  1. map 表示地图
    1. 给出地图的数组表示,想起了学习稀疏数组的时候
  2. i,j 表示从地图的哪个位置开始出发 (1,1)
    1. 确定出发点,也是确定递归的开始
  3. 如果小球能到 map[6][5] 位置,则说明通路找到
    1. 确定终点,也是确定递归的结束点
  4. 约定: 当map[i][j] 为 0 表示该点没有走过 当为 1 表示墙 ; 2 表示通路可以走 ;3 表示该点已经走过,但是走不通
    1. 对于每个含义都要赋予特定的数值表示,不可遗漏
  5. 在走迷宫时,需要确定一个策略(方法) 下->右->上->左 , 如果该点走不通,再回溯
    1. 当然是要确定行走策略的,方便实现递归

思路清晰,代码上场

package com.xy.recursion;

public class MiGong {

    public static void main(String[] args) {
        //先把迷宫建立起来
        //红色部分代表墙,用数字1表示,其余部分是暂时可走路径,用0表示
        int MG[][] = new int[8][7];
        for (int i = 0; i < 7; i++) {
            MG[0][i] = 1;
            MG[7][i] = 1;
        }
        for (int i = 0; i < 8; i++) {
            MG[i][0] = 1;
            MG[i][6] = 1;
        }
        MG[3][1] = 1;
        MG[3][2] = 1;
        System.out.println("迷宫模型展示~~");
        for (int i = 0; i < 8; i++) {
            for (int j = 0; j <7; j++) {
                System.out.print(MG[i][j]);
                System.out.print("\t");
            }
            System.out.println("\n");
        }
        setWay(MG, 1, 1);
        System.out.println("迷宫模型路线展示~~");
        for (int i = 0; i < 8; i++) {
            for (int j = 0; j <7; j++) {
                System.out.print(MG[i][j]);
                System.out.print("\t");
            }
            System.out.println("\n");
        }
        //建立好迷宫模型以后,我们就要建立规则,这样才可以让程序本身有条理的,不出差错的按照我们的意愿走下去
        //墙:1;未走过:0;走过:2;死路:3(回溯)
        //行走规则: 下->右->上->左(你可以根据你的意愿填写规则)
        //确定起点map[1][1]和终点map[6][5]        
    }


    public static boolean setWay(int[][] map,int i,int j) {//给地图模型,起始位置

        //给出递归结束的点,即终点
        if (map[6][5] == 2) {//原始值为0,为2代表走过,
            return true;
        }else {//没有到达终点,就要一直按照策略走下去,所以接下来是建立策略的,从而实现递归,而没走一步都需要检验是否已经到达终点,显然递归是容易实现这一点的
            if (map[i][j] == 0) {
                map[i][j] = 2;
                if (setWay(map, i+1, j)){
                    return true;
                }else if (setWay(map, i, j+1)) {
                    return true;
                }else if (setWay(map, i-1, j)) {
                    return true;    
                }else if (setWay(map, i, j-1)) {
                    return true;    
                }else {
                    map[i][j] = 3;
                    return false;
                }
            }else {
                return false;
            }

        }

    }

image.png

public static boolean setWay(int[][] map,int i,int j) {//给地图模型,起始位置

        //给出递归结束的点,即终点
        if (map[6][5] == 2) {//原始值为0,为2代表走过,
            return true;
        }else {//没有到达终点,就要一直按照策略走下去,所以接下来是建立策略的,从而实现递归,而没走一步都需要检验是否已经到达终点,显然递归是容易实现这一点的
            if (map[i][j] == 0) {                
                if (setWay(map, i+1, j)){
                    map[i][j] = 2;
                    return true;
                }else if (setWay(map, i, j+1)) {
                    map[i][j] = 2;
                    return true;
                }else if (setWay(map, i-1, j)) {
                    map[i][j] = 2;
                    return true;    
                }else if (setWay(map, i, j-1)) {
                    map[i][j] = 2;
                    return true;    
                }else {
                    map[i][j] = 3;
                    return false;
                }
            }else {
                return false;
            }

        }

    }

其原因是,每次判断if (map[i][j] == 0)后,下一步会进行循环,而我把赋值包含在其行走策略的代码中,导致每次都无法赋值,也就进入了死循环,出现错误
所以还是按照老师说的,提前假设可以走下一步,这样就可以提前赋值了

image.png
image.png

八皇后问题

Java 补

创建函数