大一学C的时候已经接触到汉诺塔递归的问题,当时只是简单了解过方法,最近开了算法课,打算重新捋一捋。
题目描述:
有三根柱子分别为A、B、C,柱子A上从下到上按金字塔状叠放着n个不同大小的圆盘,要把所有盘子一个一个移动到柱子C上,并且每次移动同一根柱子上都不能出现大盘子在小盘子上方,求解此过程。
(图片来源:http://www.hannuota.cn/)
刚拿到题目,我假设n为1,即拿起来放过去即可;假设n为2,先把上面的小盘子拿起放到B,再把下面的大盘子拿起放到C,最后把小盘子拿起放到C;假设n为3…
当n到5、6的时候,解答就变得困难了。我尝试开始从前面的情况中寻找规律,但好像并不可观…
汉诺塔小游戏: Tower of Hanoi.
(动手能力强的朋友可以去上面这个网站玩一下模拟的小游戏)
网上检索后发现递归是解这个题目的方法,接着我换了个思路…
从大的角度想,要把n个盘子移动到目标柱子,是不是要先解决把n-1个盘子移动过去的问题呢?…按这路子一直套娃,解决掉最小子问题即1个盘子的时候,问题似乎好像就可以化繁为简…
按这种思路,很容易就能想到设计这个递归算法的逻辑关键:
(A用From代替表示起点柱子,B用Helper代替表示辅助柱子,C用To代替表示目标柱子)
-
问题规模为n时*
- 1、From顶部的n-1个盘子移动到Helper
- 2、From最底部的大大大盘子移动到To
- 3、Helper上面的n-1个盘子移动到To 问题规模为n-1时
- 1、From顶部的(n-1)-1 个盘子移动到Helper
- 2、From底部的大大盘子移动到To
-
3、Helper上面的(n-1)-1 个盘子移动到To
…
问题规模为2时
- 1、From顶部的 1 个盘子移动到Helper
- 2、From底部的大盘子移动到To
- 3、Helper上面的 1 个盘子移动到To
来人!上图(手动起草)
这是递归算法的第一个空间栈中计算机所需要做的事情,但并不是三步即可搞定,它会从这个空间中直线引申出许多另外的空间,来解决当前空间的子问题,再一步步回代,当第一个空间中的事情解决了,问题也就解决了。
递归逻辑出来,可以根据其进行码代(分别对应上图3个步骤):
//对应图中第一步:把n-1个盘子移动到Helper
Hanoi(n-1, From, Helper, To);
//对应图中第二步:把最底下的盘子移动到To
Move(n, From, To);
//对应图中第三步:把n-1个盘子移动到To
Hanoi(n-1, Helper, To, From);
然后是递归出口的问题(即n-1为1的时候)
//出口
if (n==1) {
Move(n,From,To);
return;
}
(跑一跑代码,芜湖,迎刃而解~)
完整的代码:
(Move方法:打印解的步骤)
import java.util.*;
public class Solution {
public static void Hanoi(int n,String From,String To ,String Helper) {
//出口
if (n==1) {
Move(n,From,To);
return;
}
//对应图中第一步:把n-1个盘子移动到Helper
Hanoi(n-1, From, Helper, To);
//对应图中第二步:把最底下的盘子移动到To
Move(n, From, To);
//对应图中第三步:把n-1个盘子移动到To
Hanoi(n-1, Helper, To, From);
}
public static void Move(int n,String From,String To) {
//打印解的步骤
System.out.println("移动" + n + "号盘子从" + From + "到" + To);
}
public static void main(String[] args) {
//借助Helper,把5个盘子从From移动到To
Hanoi(5, "From", "To", "Helper");
}
}
想起我的数据结构李师之前说的一番话:
递归代码少,思路对了设计起来也是比较简单,但真正一步步去细想和演算,是非常困难的一件事情,所以把这件事交给计算机,我们做不到的它可以帮我们做到…
(完)