递归,递归方法调用,递归实现阶乘、猴子吃桃、迷宫、汉诺塔问题

基本定义

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

递归应用

  • 各种数学问题,如八皇后、汉诺塔、阶乘、迷宫、球和篮子的问题等
  • 各种算法中也会使用递归,如快排、归并排序、二分查找、分治算法等
  • 递归是用栈解决问题

阶乘递归调用

public class Test {
    public static void main(String[] args){
        int res = factorial(3);
        System.out.println(res); // 3! = 6
    }

    // 求阶乘,阶乘公式:n! = 1 * 2 * 3 * ... * n
    public static int factorial(int n){
        if(n == 1){
            return 1;
        }else {
            return factorial(n - 1) * n;
        }
    }
}

执行过程如下图:

递归的相关原则

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

递归实现斐波那契数列

斐波那契数列:1,1,2,3,5,8,13....

规律:从第三个数开始,第n个数等于前两个数的和

代码如下:

public class Test {
    public static void main(String[] args){
        int res = fibonacci(7);
        System.out.println(res); 
    }

    // 斐波那契数列:1,1,2,3,5,8,13...
    public static int fibonacci(int n){
        // 如果是 1 或者 2 ,返回 1
        if(n == 1 || n == 2){
            return 1;
        }else { // 否则返回前两个数之和
            return fibonacci(n - 1) + fibonacci(n - 2);
        }
    }
}

递归实现猴子吃桃子问题

问题描述:有一堆桃子,猴子第一天吃了其中的一半,并再多吃了一个。以后每天猴子都吃其中的一半,然后再多吃一个。当第十天时,想再吃时(但还没吃),发现只有 1 个桃子了。问:最初总共有多少个桃子?

思路分析:倒推回去,即:

  1. 第 10 天时,桃子总数为 1,那么设第 9 天时一共有 x 个桃子,则 x - (x / 2 + 1) = 1 => x = 2 * (1 + 1) = 4 个桃子,所以第 9 天一共有 4 个桃子
  2. 第 9 天时,桃子总数为 4,那么设第 8 天时一共有 x 个桃子,则 x - (x / 2 + 1) = 4 => x = 2 * (4 + 1) = 10 个桃子,所以第 8 天一共有 10 个桃子
  3. 第 8 天时,桃子总数为 10,那么设第 7 天时一共有 x 个桃子,则 x - (x / 2 + 1) = 10 => x = 2 * (10 + 1) = 22 个桃子,所以第 7 天一共有 22 个桃子
  4. 依次类推,得到以下公式:x = 2 * (n + 1),x表示某一天总共有的桃子数,n表示后一天的桃子总数。
  5. 带入问题,第 10 天总共有 1 个桃子,即第九天剩余 1 个桃子=>可以得到第九天的桃子总数,第九天的桃子总数又是第八天的剩余桃子,因此,某一天的桃子总数 = 前一天的剩余桃子。依次类推,得到第二天的桃子总数,即可求得第一天的剩余桃子,进而求得第一天的桃子总数。

如下图所示:

代码如下:

public class Test {
    public static void main(String[] args){
        int res = eatPeach(1);
        System.out.println(res); // 1534
    }

    public static int eatPeach(int day){
        // 第 10 天的桃子总数为 1
        if(day == 10){
            return 1;
        }
        // 其他天的桃子总数
        return 2 * (eat(day + 1) + 1);
    }
}

 递归实现迷宫问题

public class Test {
    public static void main(String[] args){
        // 声明一个 8*7 的二维数组
        int[][] map = new int[8][7];
        // 初始化迷宫地图
        // 把第一行和最后一行,第一列和最后一列数组元素都设置为1
        for(int i = 0;i < map[0].length;i++){
            map[0][i] = 1;
            map[7][i] = 1;
        }
        for(int i = 0;i < map.length;i++){
            map[i][0] = 1;
            map[i][6] = 1;
        }
        map[3][1] = 1;
        map[3][2] = 1;
        // map[2][2] = 1;

        // 利用递归寻找迷宫路径
        MiGong mg = new MiGong();
        mg.findWay(map, 1, 1);
        mg.printMap(map);
    }
}

class MiGong {
    // map 为迷宫地图,实际上为一个二维数组
    // i 表示当前老鼠所在行
    // j 表示当前老鼠所在列
    // 寻路策略为:下->右->上->左
    // 在map数组中,元素0:表示无障碍物可以走,1:表示有障碍物,2:表示可以走并且已走过,3表示是死路
    public boolean findWay(int[][] map,int i,int j){
        if(map[6][5] == 2){ // 到达目标点
            return true;
        }else {
            // 如果map[i][j]元素为0,表示无障碍物,可以走
            if(map[i][j] == 0){
                map[i][j] = 2; // 首先标记map[i][j]位置可以走
                // 按照寻路策略寻找下一个可以走的位置
                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; // 将该位置标记为死路
                    return false;
                }
            }else{ // 如果map[i][j]不为0,即等于1或2或3,都表示不能走
                return false;
            }
        }
    }

    // 打印地图,即打印二维数组查看情况
    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();
        }
    }
}

递归实现汉诺塔

问题描述:有三根石柱子,在一根柱子上从下往上按照大小顺序叠放着64片圆盘。现要求把圆盘从下面开始,按照大小顺序重新摆放在另一根柱子上。并且规定:在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。

代码如下:

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

class HanoiTower{
    // num:圆盘的总数
    // a:A柱子
    // b:B柱子
    // c:C柱子
    public void move(int num, char a, char b, char c){
        if(num == 1) {
            // 只有一个圆盘,则直接从 A 柱子移动到 C 柱子
            System.out.println(a + " -> " + c);
        } else {
            // 多于两个圆盘时,将圆盘分成两种:最大的圆盘和其他圆盘,其中把其他圆盘看成一个整体
            // 1.先借助 C 柱子把其他圆盘从 A 柱子移动到 B 柱子
            move(num-1, a, c, b);
            // 2、再把最大的圆盘移动到 C 柱子
            System.out.println(a + " -> " + c);
            // 3、再把其他圆盘借助 A 柱子从 B 柱子移动到 C 柱子
            move(num - 1, b, a, c);
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值