汉诺塔(Tower of Hanoi)问题是源于印度一个古老传说的益智玩具。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。——摘自百度百科
相信对于汉诺塔问题大家或多或少都了解到过,这是一道经典的递归问题。乍一看似乎计算过程十分复杂,可是如果把问题简化到更小规模的问题,以此确定递归关系,这道题的解法就会变得十分神奇。
现在,我们规定圆盘可以从一个柱子任意移到另一个柱子上。假设有N个圆盘,我们的目标是:将N个圆盘从第一个柱子移到第三个柱子。那么可以把问题分解为:
1.将N-1个圆盘从第一个柱子移到第二个柱子
2.将第N个圆盘从第一个柱子移到第三个柱子
3.将N-1个圆盘从第二个柱子移到第三个柱子
图示如下:图片来源点击跳转
按照这个思路,我们首先已经可以将所需移动的步数很快的算出来了:
设F(N)为将N个圆盘从一个柱子移到任意一个柱子所花费的步数,那么之前的步骤可以化为:
1.将N-1个圆盘从第一个柱子移到第二个柱子–>F(N-1)步
2.将第N个圆盘从第一个柱子移到第三个柱子–>1步
3.将N-1个圆盘从第二个柱子移到第三个柱子–>F(N-1)步
得出递归式:F(N)=2*F(N-1)+1
通项式:F(N)=2^N-1
那么如果我们要求出路径呢?步骤与上面分析的相同,我们现在考虑将N个圆盘从第一个柱子移到第三个柱子,那么需要依靠第二个柱子这个"中间柱",即起点:1,终点:3,中间柱:2,圆盘数:N可拆分为:
1.将N-1个圆盘从第一个柱子移到第二个柱子–>起点:1,终点:2,中间柱:3,圆盘数:N-1
2.将第N个圆盘从第一个柱子移到第三个柱子–>将N从1移到3
3.将N-1个圆盘从第二个柱子移到第三个柱子–>起点:2,终点:3,中间柱:1,圆盘数:N-1
放上代码:
void hanoi(int n,char a,char b,char c)//参数代表:起点a,终点c,中间柱b,圆盘数n
{
if(n == 1)
printf("第%d个圆盘:%c-->%c\n",n,a,c);//一个盘子的情况,直接从起点移到终点
else
{
hanoi(n-1,a,c,b); //先将起点a上的n-1个圆盘借助c移到中间柱b
printf("第%d个圆盘:%c-->%c\n",n,a,c); //再将最后一个盘子从起点a移到终点 c
hanoi(n-1,b,a,c); //最后将中间柱b上的n-1个盘子借助a移动到c
}
理解到这里已经能解决最基本的汉诺塔问题啦:)
那么我们将问题升级一下:如果圆盘只能在相邻的柱子上移动呢?
其实解决思想还是一样的,只不过步骤稍微复杂了一些:
1.将N-1个圆盘从第一个柱子移到第三个柱子
2.将第N个圆盘从第一个柱子移到第二个柱子
3.将N-1个圆盘从第三个柱子移到第一个柱子
4.将第N个圆盘从第二个柱子移到第三个柱子
5.将N-1个圆盘从第一个柱子移到第三个柱子
在此就不多赘述了,相信大家如果理解了基础的汉诺塔问题的话这个也能自行很容易读懂了
我们可以同样的得出递归式:F(N)=3*F(N-1)+2
求路径的代码:
void hanoi(int n,char a,char b,char c)
{
if(n == 1)
{
printf("第%d个圆盘:%c-->%c\n",n,a,b);
printf("第%d个圆盘:%c-->%c\n",n,b,c);
}
else
{
hanoi(n-1,a,b,c);
printf("第%d个圆盘:%c-->%c\n",n,a,b);
hanoi(n-1,c,b,a);
printf("第%d个圆盘:%c-->%c\n",n,b,c);
hanoi(n-1,a,b,c);
}
}