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语言中,不管参数的类型,是引用类型还是,基本数据类型,实际参数和形式参数进行值传递的方式只有一种:
实际参数的值 复制一份 赋值给形式参数
所以,实参的值,其实就有两份,调用方法中一份,被调用方法中一份
- 当方法的参数是 基本数据类型 的参数的时候,参数有两份,同时参数对应的数据的值,也有两份。
- 当方法的参数是引用数据类型的时候参数值有两份,但是两个数组类型引用变量,对应的值(数组),只有一个。
5.递归例子:
①汉诺塔问题:
有三根杆子A,B,C。A杆上有 N(64) 个 (N>1) 穿孔圆盘,盘的尺寸由下到上依次变小。要求按下列规则将所有圆盘移至 C 杆:
规则:
a.每次只能移动一个圆盘;
b大盘不能叠在小盘上面。
提示:可将圆盘临时置于 B 杆,也可将从 A 杆移出的圆盘重新移回 A 杆,但都必须遵循上述两条规则。
问:最少要移动多少次?如何移?
算法思想:
解决思路以N个圆盘为例:
- 当我们要解决N个圆盘的搬运问题,对于N个圆盘,我们可能无法一次性得到结果,于是,我们把N个圆盘的搬运问题转化为:最大一个的圆盘的搬运 & 最大圆盘上面的 n - 1个圆盘搬运。
- 对于规模为1那个待搬运的最大的圆盘,直接就知道如何搬运(一步搞定)
- 在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;
}
}