JAVA 递归


方法递归调用

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

1.递归能够解决什么问题?

image-20220115205607679

例子:

1.打印问题

public void test(int n) {
    if(n>2) {
        test(n-1);
    }
    System.out.println("n=" + n);
}

2.阶乘的执行机制

//factorial 阶乘
public int factorial(int n) {
    if(n==1) {
        return 1;
    }else {
        return factorial(n-1)*n;
    }
}

2.递归的重要规则

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

斐波拉切数

题目:请使用递归的思路方式,求出斐波拉契数1,1,2,3,5,8,13...给你一个整数,求出它的值是多少

package Recursion_Practice;

public class RecursionExercise01 {
    public static void main(String[] args) {
        A a = new A();
        int n = 10;
        int ret = a.fibonacci(n);
        if(ret != n) {
            System.out.println("当n=时" + n + "对应的斐波拉契数是" + ret );
        }else {
             System.out.println(n+"不是斐波拉契数,要求输入的n>=1");
        }
    }
}

class A{
    /*
     * 请使用递归的思路方式,求出斐波拉契数1,1,2,3,5,8,13...给你一个整数,
     * 求出它的值是多少
     * 
     * 思路分析
     * 1.当n=1时 斐波拉契数=1
     * 2.当n=2时 斐波拉契数=1
     * 3.当n=3时 斐波拉契数=2
     * 即当n>=3时 斐波拉契数前两个数的和
     * 这里就是递归的思想 
     */
     public int fibonacci(int n) {
         if( n >= 1) {
             if( n == 1 || n == 2) {
                 return 1;
             }else {
                 return fibonacci(n-1) + fibonacci(n-2);
             }
         }else {
             return -1;
         }
     }
}

猴子吃桃

题目:猴子第一天摘下若干个桃子,当即吃了一半,还不过瘾,又多吃了一个。第二天早上又将第一天剩下的桃子吃掉一半,有多吃了一个。以后每天早上都吃了前一天剩下的一半零一个。到第 10 天早上想再吃时,发现只剩下一个桃子了。编写程序求猴子第一天摘了多少个桃子

package Recursion_Practice;

public class RecursionExercise01 {
    public static void main(String[] args) {
        A a = new A();
        //斐波拉契数列问题
        int n = 10;
        int ret = a.fibonacci(n);
        if(ret != -1) {
            System.out.println("当n=时" + n + "对应的斐波拉契数是" + ret );
        }else {
             System.out.println(n+"不是斐波拉契数,要求输入的n>=1");
        }
        
        //猴子吃桃问题
        int day = 1;
        int peach = a.peach(day);
        if(peach != -1) {
            System.out.println(day+"天有" + peach + "个桃子");
        }else {
            System.out.println("请输入正确的天数(1~10)");
        }
        
    }
}

class A{
    /*
     * 请使用递归的思路方式,求出斐波拉契数1,1,2,3,5,8,13...给你一个整数,
     * 求出它的值是多少
     * 
     * 思路分析
     * 1.当n=1时 斐波拉契数=1
     * 2.当n=2时 斐波拉契数=1
     * 3.当n=3时 斐波拉契数=2
     * 即当n>=3时 斐波拉契数前两个数的和
     * 这里就是递归的思想 
     */
     public int fibonacci(int n) {
         if( n >= 1) {
             if( n == 1 || n == 2) {
                 return 1;
             }else {
                 return fibonacci(n-1) + fibonacci(n-2);
             }
         }else {
             return -1;
         }
     }
     
     /*
      * 猴子第一天摘下若干个桃子,当即吃了一半,还不过瘾,又多吃了一个。
      * 第二天早上又将第一天剩下的桃子吃掉一半,有多吃了一个。
      * 以后每天早上都吃了前一天剩下的一半零一个。
      * 到第 10 天早上想再吃时,发现只剩下一个桃子了。
      * 编写程序求猴子第一天摘了多少个桃子
      * 
      * 1. day=10时 有1个桃子
      * 2. day=9时  有(day10+1)*2 = 4 设方程求解也是可以的得到这个关系式
      * 3. day=8时  有(day9+1)*2 = 10
      * 规律就是 当前天数的桃子 = (后一天的桃子 + 1) *2;
      */
     
     //求当前天数的桃树
     public int peach(int day) {
         if (day == 10) {
             //第十天只有一个桃
             return 1;
         }else if(day >=1 && day <=9){
             return (peach(day+1)+1)*2; 
         }else {
             return -1;
         }
     }
}

运行效果:

image-20220116154734099


迷宫问题

代码如下:

package Recursion_Practice;

public class MiGong {
    public static void main(String[] args) {
        /*
         * 思路
         * 1.先创建迷宫,用二维数组表示 int map[][] = new int[8][7]
         * 2.先规定 map 数组的元素值: 0表示可走的路,1表示障碍物
         * 3.将最上面的一行和最下面的一行,全部设置为1
         */
        
        Tool tool = new Tool();
        int map[][] = new int[8][7];
        //把行和列外面那一圈都设为障碍物
    
        tool.setBar(map);
        tool.printMap(map);
        
        tool.findWay(map, 1, 1);
        System.out.println("\n === 找路情况如下");
        tool.printMap(map);
    }
}

class Tool{
    
    //输出当前地图
    public void printMap(int map[][]) {
        //当前地图情况
        System.out.println("==当前地图情况==");
        for (int i = 0; i < map.length; i++) {
            for (int j = 0; j < map[i].length; j++) {
                System.out.print(map[i][j]+" ");
            }
            System.out.println();
        }
    }
    
    //设置障碍物
    public void setBar(int map[][]) {
        for (int i = 0; i < 7 ; i++) {
              map[0][i] = 1;
              map[7][i] = 1;
              map[i][0] = 1;
              map[i][6] = 1;
        }
        map[3][1] = 1;
        map[3][2] = 1;
    }
    
    /*
     * 使用递归回溯的思想来解决老鼠出迷宫
     * 
     * 1.findWay 方法就是专门来找出迷宫的路径
     * 2.如果找到,就返回true,否则返回false
     * 3.map 就是二维数组,即表示迷宫
     * 4.i,j 就是老鼠的位置,初始化的位置为(1,1)
     * 5.因为是递归的找路,所以我们规定 map 数组的各个值的含义
         * 0表是可以走
         * 1表示障碍物
         * 2表是可以走
         * 3表示走过,但是死路
     * 6.当map[6][5] = 2时 就说明找到通路了,否则就继续找
     * 7.先确定老鼠找路的策略 下->右->上->左 
     *
     */
    
    public boolean findWay(int map[][],int i, int j) {
        
        if(map[6][5] == 2 ) {
            //说明已经找到路了
            return true;
        }else {
            if(map[i][j] == 0) {
                //当前这个位置0,说明可以走
                //我们假定可以走通
                map[i][j] =2;
                //使用找路的策略,来确定该位置是否真的可以走通
                //下->右->上->左
                if(findWay(map,i+1,j)) {//先向下走
                    return true;
                }else if(findWay(map,i,j+1)) {//向右走
                    return true;
                }else if(findWay(map,i-1,j)) {//向上走
                    return true;
                }else if(findWay(map,i,j-1)) {//向左走
                    return true;
                }else {
                    map[i][j] = 3;//3表示走过但是死路,表示走不通
                    return false;
                }
                
            }else {
                //map[i][j] = 1|| map[i][j] =2 || map[i][j] =3
                //这些点都是不能走的 return false即可
                return false;
            }
        }
    }
    
}

汉罗塔

package Recursion_Practice;

public class HannoiTower {
    public static void main(String[] args) {
        Tower tower= new Tower();
        tower.move(5, 'A', 'B', 'C');
    }
}


class Tower{
    /*
     * num表示要移动的个数
     * a,b,c分别表示A、B、C柱
     * 思路:
     * 令总共有n个盘子
     * 1.先把n-1个盘子移动到辅助用的柱子上 b
     * 2.然后把最下面的那个盘子移动到 c 柱子上去
     * 3.再把b柱子上的盘子移到c柱子上去
     */
    public void move(int num, char a, char b,char c) {
        if(num == 1) {
            System.out.println(a + "->" + c);
        }else {
            //如果有多个盘,可以看成两个盘,最下面的和上面的所有
            //1)移动上面的所有盘到b,借助c
            move(num - 1 , a , c ,b); 
            //2)把最下面的这个盘移动到c
            System.out.println(a + "->" + c);
            //3)再把b塔的所有盘,移动动到c,借助a
            move(num - 1 , b , a ,c);
            
        }
    }
}