一、前言
汉诺塔(Tower of Hanoi),又称河内塔,是一个源于印度古老传说的益智玩具。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。
二、问题分析
假设要将n个盘子从A移动到C,我们可以先将n设为3,可以分为上动图的七步进行,读者可以尝试将n设为一个10,尝试将每一步写出来,很明显写出每一个步骤非常复杂,这时我们就需要对问题进行思考,更改思路,将复杂的问题简单化。
我们先将n设定为2,这时我们可以发现整个过程变得非常简单,先将上层盘子从A移到B,再将下层盘子从A移到C,最后将B上的盘子移到C就完成了整个步骤,因此对于n个盘子来说,我们可以先将上方的n-1个盘子看作一整个盘子,这时就变成了移动两个盘子的简单问题。
我们继续思考,如果我们先将上方的n-1个盘子移动到了B,这时可以将A的一个盘子移到C,但是我们直接移动B上的n-1个盘子是不现实的,这时我们可以再将n-2个盘子看作一个整体,先倒数第二个盘子放到C上,这时如下图所示。
此时我们要将第n-1个盘子移到C可以先将上面的n-2个盘子移到A,在移动第n-1个盘子到C。
此时除去最右边两个盘子不看,A上的盘子又和初始状态一样,这样不断地进行递归,直到将第一个盘子移到C上,问题就解决了,不难看出,任务完成的条件是只移动一个盘子到C,否则递归将进行下去。
三、代码编写+分析
分析过后,开始编写,我们可以先将打印盘子移动过程的函数编写
void move(char x, char y){printf("从%c移到%c\n", x, y);}//定义move函数,表明将1个盘子从x座移到y座的过程,根据情况带入ABC
注意,上述的函数只对移动过程进行打印,不进行移动操作,接下来对移动盘子的函数进行编写。
void hanoi(int n, char a, char b, char c){//上一行表明定义hanoi函数,n个盘子,盘子从A借助B移动到Cvoid move(char a, char c);//声明move函数if (n == 1)move(a, c);else{hanoi(n - 1, a, c, b);move(a, c);hanoi(n - 1, b, a, c);}}
定义一个函数,根据上述两图分析的过程,第一次递归使用hanoi函数是将n-1个盘子从A借助C移动到B,第二行是将A移动到C的步骤打印出来(并未真正的移动盘子,只是输出移盘的方案),第二次使用递归是将n-1个盘子从B借助A移到C,然后每次移动n-1个盘子都要对n-2个盘子进行操作,持续递归直到只移动一个盘子,至此任务完成。
最后是主函数与上述函数整合在一起。
#include int main(){void hanoi(int n, char a,char b,char c);//声明hanoi函数int m;printf("请输入总数:");scanf("%d", &m);printf("移动步骤为:\n");hanoi(m, 'A', 'B', 'C');return 0;}void hanoi(int n, char a, char b, char c){void move(char a, char c);if (n == 1)move(a, c);else{hanoi(n - 1, a, c, b);move(a, c);hanoi(n - 1, b, a, c);}}void move(char x, char y){printf("从%c移到%c\n", x, y);}
输出结果:
四、故事分析
通过上述操作我们可以得知移动盘子所需要的步骤为2^n-1,大梵天要是让婆罗门移到64个盘子的话,也就是需要2^64-1步,算出来是18446744073709551615,这也是有人戏称,婆罗门转移完64个盘子的时候,“世界末日”也就要到了的原因。