Java递归

1.递归定义:

方法定义中调用方法本身的现象。

2.实现递归的注意事项:

① 递归一定要有出口!!(在某种特定的情况下,停止自己调用自己, 并结束方法)。
②次数不能太多,否则就出现栈溢出( stack
overflow)。

3.JVM栈空间的内存分配:

① 栈空间内存分配的基本单位

a. 局部变量(方法的形式参数和方法中定义的变量)是存储在栈空间中。
b. 一个方法,只有当它被调用执行的时候,方法中的局部变量等,才需要在栈空间上存储。即每一个运行中的方法,都需要占用栈内存中的一片存储空间。
c. 于是,每一个运行中的方法,都会在栈上分配一片,只属于该运行中的方法的内存空间 — 栈帧。

② 栈空间中的内存分配和回收

a. 当一个方法被调用执行的时候,给该运行中的方法,分配栈帧。
b. 当一个方法执行完毕的时候,它所对应的栈帧被回收(销毁)。

注意 :同一个方法多次执行,每一次执行的时候,都会在栈上分配一个栈帧

4.关于参数传递的问题:

以下面一个例子说明:

public class demo1 {
    public static void main(String[] args) {
        int a = 10;
        int b = 20;
        System.out.println("----------方法的参数是基本数据类型-------------");
        System.out.println("调用前:a:" + a + ",b:" + b);
        //调用change()方法改变a,b的值
        change(a, b);
        System.out.println("调用后:a:" + a + ",b:" + b);

        System.out.println("----------方法的参数是引用数据类型---------");
        int[] arr = {1, 2, 3, 4, 5};
        System.out.println("调用前:"+Arrays.toString(arr));
        change(arr);
        System.out.println("调用后:"+Arrays.toString(arr));
    }


    public static void change(int a, int b) {
        System.out.println("a:" + a + ",b:" + b);
        a = b;
        b = a + b; // b = b + b
        System.out.println("a:" + a + ",b:" + b);
    }


    public static void change(int[] arr) {
        for (int x = 0; x < arr.length; x++) {
            if (arr[x] % 2 == 0) {
                arr[x] *= 2;
            }
        }
    }
}

输出结果:
在这里插入图片描述
总结一下:
在java语言中,不管参数的类型,是引用类型还是,基本数据类型,实际参数和形式参数进行值传递的方式只有一种:
实际参数的值 复制一份 赋值给形式参数
所以,实参的值,其实就有两份,调用方法中一份,被调用方法中一份

  1. 当方法的参数是 基本数据类型 的参数的时候,参数有两份,同时参数对应的数据的值,也有两份。
  2. 当方法的参数是引用数据类型的时候参数值有两份,但是两个数组类型引用变量,对应的值(数组),只有一个。

5.递归例子:

①汉诺塔问题:
有三根杆子A,B,C。A杆上有 N(64) 个 (N>1) 穿孔圆盘,盘的尺寸由下到上依次变小。要求按下列规则将所有圆盘移至 C 杆:

规则:
a.每次只能移动一个圆盘;
b大盘不能叠在小盘上面。

提示:可将圆盘临时置于 B 杆,也可将从 A 杆移出的圆盘重新移回 A 杆,但都必须遵循上述两条规则。
问:最少要移动多少次?如何移?
算法思想:
解决思路以N个圆盘为例:

  1. 当我们要解决N个圆盘的搬运问题,对于N个圆盘,我们可能无法一次性得到结果,于是,我们把N个圆盘的搬运问题转化为:最大一个的圆盘的搬运 & 最大圆盘上面的 n - 1个圆盘搬运。
  2. 对于规模为1那个待搬运的最大的圆盘,直接就知道如何搬运(一步搞定)
  3. 在2的基础上,只需要,再解决 N - 1个圆盘搬运的问题即可。

代码:

public class Demo2 {

    public static void main(String[] args) {

        // 输出汉诺塔问题的搬运序列
        hanoi(3, 'A', 'C', 'B');

        // 计数汉诺塔问题搬运的总次数
        long count = countHanoi(4);
        System.out.println("搬运总次数:"+count);
    }

    // n表示汉诺塔问题中,待搬运圆盘的数量
    public static long countHanoi(int n) {
        // 递归出口
        if (n == 1) {
            return 1;
        }
        //将最大的圆盘的之上的n-1个盘,搬运到辅助杆上
        return countHanoi(n - 1) + 1 + countHanoi(n - 1);
    }

    /**
     * @param n      待搬运的圆盘数量
     * @param start  待搬运的圆盘所在的杆的名子(起始杆)
     * @param end    待搬运的圆盘,所要搬运到的目标杆的名字
     * @param middle 在本次搬运过程中所使用的辅助杆的名字
     */
    public static void hanoi(int n, char start, char end, char middle) {

        //递归出口
        if (n == 1) {
            // 直接解决规模为1的子问题
            System.out.println(start + " -> " + end);
            return;
        }

        //此时问题还可以分解为子问题

        //将最大的圆盘的之上的n-1个盘,搬运到辅助杆上
        hanoi(n - 1, start, middle, end);

        //解决,最大的哪一个盘的搬运问题
        System.out.println(start + " -> " + end);

        //将辅助杆上的n-1个盘,搬运到目标杆上去(以原来的start杆为辅助)
        hanoi(n - 1, middle, end, start);
    }


}

输出结果:
在这里插入图片描述

一个楼梯有n (n >= 1)级,每次走1级或两级,请问从1级台阶走到第n级台阶一共有多少种走法(假设一开始站在第0级台阶上)
代码:

public class ClimbTheStairs {
    public static void main(String[] args) {
        int n;//楼梯数
        System.out.println("输入楼梯数:");
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        System.out.println(n+"级楼梯共有"+climbTheStairs(n)+"种走法。");
    }
    public static int climbTheStairs(int n){
        int count = 0;//可能走法数
        //有一级楼梯或二级楼梯
        //一级楼梯:走法=1
        //二级楼梯:走法=2
        if(n == 1 || n == 2) {
            return n;
        }
        //有大于两级楼梯
        //可以先走一步,也可以先走两步
        if(n > 2) {
            count = climbTheStairs(n - 1) + climbTheStairs(n - 2);
        }
        return count;
    }
}


计算n条直线最多能把平面分成多少部分? n >= 1
代码:

public class LineDivisionPlane {
    public static void main(String[] args) {
        int lines;//直线数
        System.out.println("输入直线数:");
        Scanner sc = new Scanner(System.in);
        lines = sc.nextInt();
        System.out.println(lines+"条直线最多分割"+lineDivisionPlane(lines)+"个平面。");
    }
    public static int lineDivisionPlane(int lines){
        int count = 0;//分割平面数
        //一条直线分割2个平面
        if(lines == 1) {
            return 2;
        }
        //多条直线
        if(lines > 1) {
            count = lineDivisionPlane(lines - 1) + lines;
        }
        return count;
    }
}


猴子第一天摘了若干个桃子,当即吃了一半,还不解馋,又多吃了一个;
第二天,吃剩下的桃子的一半,还不过瘾,又多吃了一个;
以后每天都吃前一天剩下的一半多一个,到第10天想再吃时,只剩下一个桃子了。
问第i(i的取值范围为[1, 10])天的桃子个数?
代码:

public class MonkeysEatPeaches {
    public static void main(String[] args) {
        int day;//第几天
        System.out.println("输入第几天:");
        Scanner sc = new Scanner(System.in);
        day = sc.nextInt();
        if( day >=1 && day <=10) {
            System.out.println("第"+day+"天有"+monkeysEatPeaches(day)+"个桃子。");
        }else {
            System.out.println("输入错误!");
        }
    }
    public static int monkeysEatPeaches(int day) {
        int count = 0;//桃子数目
        //第10天桃子数目
        if(day == 10) {
            return 1;
        }
        if(day >= 1 ||day <= 9) {
            count = (monkeysEatPeaches(day + 1) + 1) * 2;
        }
        return count;
    }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值