前言
小时候在直板手机上玩过这么一款叫做“汉诺塔”的游戏,游戏方式很简单,通过上下左右四个按键将一堆大小有序、垂直排列的圆盘从一根柱子上移动到另一根柱子上,小时候也懒得思考就是在瞎玩。现在用递归的思想来解决一下这个问题,我尽可能把我自己的理解方式通俗的讲给大家,如有不对的地方,请大家批评指正!
一、汉诺塔问题
我们就依照这个图上所说的64个圆片来解决问题(图片出处:刘铁猛老师)
二、问题思考
1.简单定义一下
这里为了表达更清楚,我们定义一下:
三根柱子分别为A柱,B柱,C柱。
Px代表柱上的圆片,P1代表从上至下第一个圆片(也就是最小的那个圆片),同理,P64代表从上至下最后一个圆片(也就是最大的那个圆片)。
Nm~n代表把Pm与Pn(包括Pm和Pn本身)之间所有的圆片移动到另外一根柱子上所用的次数。
例如,N1~63代表把P1与P63(包括P1和P63本身)之间所有的圆片移动到另一根柱子上所用的次数。
2.假设简单的情况
这里我们先来假设一种比较简单的情况,如图,三根柱子(A,B,C),3个圆盘,P1,P2,P3
那么我们思考一下,想把全部的圆盘移动到另外一根柱子上需要怎么做?
首先:把P1和P2移动到C柱上,这样做的目的是为了把P3暴露出来以便可以移动(这里先不管具体操作了多少步,而是从整体上考虑应该怎么做)
其次:把P3移动到B柱上
最后:把P1和P2移动到B柱上
至此,我们一共做个3件事情:
1.把除了最大圆盘以外的其余圆盘从A柱移动到C柱(即:把P1和P2从A柱移动到C柱)
2.把最大圆盘从A柱移动到B柱(即:把P3从A柱移动到B柱)
3.把除了最大圆盘以外的其余圆盘从C柱移回到B柱(即:把P1和P2从C柱移动到B柱)
到这里我们应该能发现两个比较重要的问题:
(1)如果我们把除最大圆盘以外的其他圆盘看作一个整体,那么这个整体被移动了两回(从A柱到C柱,从C柱到B柱),我们假设这个整体被移动一回所需要的次数为N
(2)当(1)中的整体被移动以后,所暴露出来最大的那个圆盘,只需要移动一次(从A柱到C柱)
so,我们完成整个流程所需要的次数是N+1+N(移动整体+移动最大的+移动整体)。
but,这个整体怎么办呢?这个整体就接着用这个方法再去解决,一层一层的,这就是递归的思想啦,我们接下来用题目的例子详细讲一下
3.推广
现在我们来看题目中的问题,64个圆盘,3根柱子
依照我们之前的思想,现在我们解决N1~64(即:整体移动P1至P64所需次数)
(1.1)把P1至P63看作一个整体,移动到另外一个柱子上,所需次数为N1~63
(1.2)接着移动P64,所需次数为1次
(1.3)把P1至P63移动回来,所需次数为N1~63
(1.4)N1~64 = N1~63 + 1 + N1~63
N1~63怎么算?(即:整体移动P1至P63所需次数)
(2.1)把P1至P62看作一个整体,移动到另外一个柱子上,所需次数为N1~62
(2.2)接着移动P63,所需次数为1次
(2.3)把P1至P62移动回来,所需次数为N1~62
(2.4)N1~63 = N1~62 + 1 + N1~62
N1~62怎么算?(即:整体移动P1至P62所需次数)
·
·(…此处省略中间的步骤…)
·
N1~2怎么算?(即:整体移动P1至P2所需次数)
(63.1)把P1看作一个整体,移动到另外一个柱子上,所需次数为N1
(63.2)接着移动P2,所需次数为1次
(63.3)把P1移动回来,所需次数为N1
(63.4)N1~2 = N1 + 1 + N1
N1怎么算?(即:整体移动P1所需次数)
(64.1)N1就很简单了,那当然就是1啦!
(64.2)N1~2 = N1 + 1 + N1
4.核心代码
public int Hanoi(int num)//参数num代表圆盘数
{
if(num == 1)
{
return 1;
}
else
{
//特别提醒:下面的result用的是int类型,如果参数num输入的是题目中的64,得到的结果是不正确的,因为已经超过了int类型的最大值,因此,需要变为long型或者double型来接收结果或者将参数num减小
//1和2方法都可以,但2似乎更快?
//1.
int result = Hanoi(num - 1) + 1 + Hanoi(num - 1);
//2.
int result = Hanoi(num - 1)*2 + 1;
return result;
}
}