第一篇 C语言——汉诺塔(Tower of Hanoi) 问题解析

汉诺塔(Hanoi)

	多图警告!

历史背景

法国数学家爱德华·卢卡斯曾编写过一个印度的古老传说:在世界中心贝拿勒斯(在印度北部)的圣庙里,一块黄铜板上插着三根宝石针。印度教的主神梵天在创造世界的时候,在其中一根针上从下到上地穿好了由大到小的64片金片,这就是所谓的汉诺塔。不论白天黑夜,总有一个僧侣在按照下面的法则移动这些金片:一次只移动一片,不管在哪根针上,小片必须在大片上面。僧侣们预言,当所有的金片都从梵天穿好的那根针上移到另外一根针上时,世界就将在一声霹雳中消灭,而梵塔、庙宇和众生也都将同归于尽。
不管这个传说的可信度有多大,如果考虑一下把64片金片,由一根针上移到另一根针上,并且始终保持上小下大的顺序。这需要多少次移动呢?这里需要递归的方法。假设有n片,移动次数是f(n).显然f(1)=1,f(2)=3,f(3)=7,且f(k+1)=2*f(k)+1。此后不难证明f(n)=2^n-1。n=64时,假如每秒钟一次,共需多长时间呢?一个平年365天有31536000秒,闰年366天有31622400秒,平均每年31557600秒,计算一下:18446744073709551615秒(以上摘自百度)

问题解析

首先汉诺塔的样子是这样的:
汉诺塔

  • 现在就需要我们解决这个问题,思考一下,如何将A上的所有小片移动到C上呢。这里就需要了解规则,首先每次只能移动一片,还有就是小片在上大片在下,这两个规则不明白的可以去链接里的小游戏自己去试一下 汉诺塔小游戏

一层汉诺塔

现在知道了规则,如何实现想要的效果呢,首先画一张图(画的比较抽象,见谅哈)。

简单示意

  • 一层的时候其实很好解决,只需要把A移动到C上就OK啦。

两层汉诺塔

现在我们来看两层的情况,同样做个图。
两层汉诺塔

  • 其实也是非常简单的,首先让小片移动到B上,再将大片移动到C上,再把小片从B移动到C上就完成了。(A->B , A->C , B->C)
  • 建议反复在大脑中模拟移动过程,对后面的更多层的移动有好处。

三层汉诺塔

有三层的情况下怎么办呢,我们仍旧是做一个图,这次标上号。
三层汉诺塔

三层的稍微有一丢丢的难度,如果不明白规则的可以去上面的链接里玩几次,就明白规则了。

  1. 如何将三片从A柱移动C柱上呢?首先是要让P1到C柱上,思考一下,为什么呢? 其实道理很简单,如果C柱上有其他的片,那么P1是不可能移动到它上面的,因为P1是最大的这一片。如果看不明白反复读几遍,脑子里模拟一下样子。
  2. 理解这个问题后就可以换位思考一下,既然要移动P1,那么P2和P3是不是就必须在B柱上呢。那么现在问题就变成了:如何把P2和P3移动到B柱上的问题。
  3. P2和P3这两片的问题,是不是非常类似于前面的两层汉诺塔问题,确实与两层的时候简直一毛一样!我们先将P3移动到C柱上,再把P2移动到B柱上,再把C柱上的P3移动回P2上,就完成了移动到B柱的任务。

如果你不能理解,那我再换一个方法:

  • P1是最大的片,那么换言之,任何片都可以放在P1上,也就可以变相的理解为P1现在是不存在的。只有P2和P3,那么就回到两层的问题上,我们最基础的移动是把两片移动到最远的地方,也就是移动到C柱上,那么我把BC柱子换个位置。是不是就是一样了。柱子(A-C-B)这样摆放。

把P2,P3从A柱移动到B柱上

  1. 接下来呢就是把A柱上的P1移动到C柱上,这个没啥好解释的,我们的目的就是A->C的移动。

到现在A柱上为空,B柱上有P2和P3,C柱上有P1。

  1. 接下来就是把B柱子上的P2,P3移动到C柱上,同样的思想,把AB柱换位,P1忽略。是不是就又变成最简单的两层汉诺塔的移动了呢。这样我们就达到了目的,将A柱上的所有片都移动到了C柱上。柱子(B-A-C)这样摆放。

将P2,P3从B柱上移动到C柱上
至此三层汉诺塔大功告成!

规律总结

  • 如果有4层5层…一直到n层呢,总不能一直这样写下去吧。

  • 咱们观察其中的规律,每次的移动其实都是先让A柱上的n-1片,先移动到B柱上,再把第n片移动到C柱上,再把B柱上的n-1片移动到C柱上即完成功能。

话不多说,直接上图:
多层情况下

  • 一层一层的拆解,想要移动n-1层的片到B柱上,就需要搬走n-2片到C柱上…这句话比较难理解,多想几次,就能明白了。同理我们把B柱上的n-1片移动到C柱上就完成了。
  • 我们不难发现,每次的循环都是同样的动作,这就刚好是计算机喜欢干的事情。我们就可以用递归来解决这个问题!

代码如下:

// 例子7.7(Hanoi汉诺问题)
int main() {
    void hanoi(int n, char one, char two, char three);
    int m;
    scanf("%d", &m);
    hanoi(m, 'A', 'B', 'C');
    return 0;
}
//n为片数,ABC代表A柱B柱C柱,含义是:把A柱上的n片,借用B柱移动到C柱上
void hanoi(int n, char A, char B, char C) {		
    void move(char x, char y);
    if (n == 1) move(A, C);	//递归出口(退出的条件,不可缺少哦!)
    else {
        hanoi(n - 1, A, C, B);      //步骤一 按ACB顺序执行N-1的移动
        move(A, C);     //步骤二 执行最大盘子移动
        hanoi(n - 1, B, A, C);      //步骤三 按BAC顺序执行N-1的移动
    }
}
void move(char x, char y) {
    printf("%c->%c\n", x, y);	//此处的移动并未真实移动,实际上是输出步骤而已
}

其实我当时最迷惑的是为啥每次都是A移动到C,但是每次移动的步骤不一样呢,这里我解释一下,因为在else中.也就是步骤一那里,在柱子(虚实结合处)进行了交换,每次虽然都是执行最简单的移动,但是每次移动的方向不一样,所以移动的结果也就不一样。

相关链接

如果你喜欢较个真,想看更加深层次的递归过程:
链接挂在这里,里面有解析,也有递归的过程,讲得很细致,建议倍速一哈:
懒猫老师 C语言 汉诺塔问题解析 15分15秒处开始深层次解析

还有其他几个Up我觉得讲的很不错,大家可以参考一下:
李永乐老师 汉诺塔问题解析
国外大佬的解析 以二进制(位运算)的角度解析,含有动画!!!

以上呢就是我对问题的理解,大家发现错误或者觉得有疑问可以留言或者私信,欢迎大家讨论。(本人菜鸡,大神别嫌弃我哈哈哈)

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值