递归学习 及汉诺塔算法
递归指的是方法自己调用自己。
当一个问题规模过大,而问题可以拆分成一个规模更小的子问题时我们便可以考虑使用递归。
编写递归代码时需要注意的问题
- 递归总有一个最简单的情况(结束条件),即我们编写的递归函数需要一条语句来判断问题是否达到了结束条件,如若达到则退出函数(所以我们在编写递归函数时通常需要找到结束条件的情况,确定函数退出的条件)
- 每次递归调用都是去解决一个规模更小的情况,只有这样我们才能通过不断地递归调用来将问题规模收敛到最小规模
- 递归调用的父问题和尝试解决的子问题之间不应该有交集
关于递归的一个经典问题就是汉诺塔问题
**即有三根杆子A,B,C。A杆上有 N 个 (N>1) 穿孔圆盘,盘的尺寸由下到上依次变小。要求按下列规则将所有圆盘移至 C 杆:
- 每次只能移动一个圆盘;
- 大盘不能叠在小盘上面。
提示:可将圆盘临时置于 B 杆,也可将从 A 杆移出的圆盘重新移回 A 杆,但
都必须遵循上述两条规则。
问:如何移?最少要移动多少次?
关于这个问题我们首先确定结束条件的情况:
可以看出当A柱上没有圆盘时我们即移动成功,所以结束条件为A柱上的圆盘数量为0
接着缩小问题规模:
可以看出当圆盘数量为n时,我们需要将n-1个圆盘移动到B柱后才能将最后一个圆盘从A柱移动到C柱,所以此时问题的规模缩小为将n-1个圆盘从A柱移动到B柱,再将问题规模不断缩小,最终变成将第n-(n-2)个圆盘移动到另一个柱子上(即两个圆盘的情况)
当有两个圆盘时,我们需要移动的次数如下
- 将小圆盘从A移动到B
- 将大圆盘从A移动到C
- 将小圆盘从B移动到C
所以代码可以编写如下:
/**
*
* @param n 圆盘总数
* @param a A柱 需要移动的圆盘放置的柱
* @param b B柱 辅助移动圆盘的柱
* @param c C柱 最终圆盘想要移动到的柱
*/
public static void hanoi(int n,String a,String b,String c) {
//计算圆盘从A柱移动到C柱所需要的次数
//当n = 0时,A柱圆盘数量为0,达到函数的退出条件
if (n > 0) {
/*
* 问题的更小规模,即需要先把最后一个圆盘上面的
* n-1个盘通过c辅助从a盘移动到b盘
* */
hanoi(n-1,a,c,b);
//将第n个盘从a柱移动到c柱
System.out.println("move "+n+" from "+a+" to "+c);
/*
* 将b盘的n-1个盘子通过a盘辅助移动到c盘上
* */
hanoi(n-1,b,a,c);
}
}
计算结果:
move 1 from a to c
move 2 from a to b
move 1 from c to b
move 3 from a to c
move 1 from b to a
move 2 from b to c
move 1 from a to c