汉诺塔问题起源于很久很久以前(反正很久)的一个古印度神话,印度教的创造之神梵天,在创造世界的时候创造出了三根金刚石柱子(钻石柱子嘛),其中一个上面有64个黄金圆环,按照从小到大的顺序套在上面,如下图,梵天命令一个叫婆罗门的教徒将所有的圆环移动到另外一个柱子上,并且满足如下规则:
- 每次只能移动柱子最顶端的一个圆盘;
- 每个柱子上,小圆盘永远要位于大圆盘之上;
图像如下
要我们算出次数最少的移动路径并且计算次数,看起来确实是一个极其困难的问题,那么,我们如何用代码解决这个问题呢?
1.简化问题;
我们先看下面这个草图,我们将三根柱子标号:a,b,c.原本的圆环在a柱子上面,我们要在满足规则的情况下将其移动到c柱子上面,那我们就将这种 移动模式 记作:
a->c的汉诺塔移动(纯属方便后面的讲解)
然后,无论上面有多少个圆环,我们先将他们分成两个部分(上面的所有圆环当作一个整体,和最下面的环) ,等价于只有两个圆环的汉诺塔问题。
2.分析简单的情况
在1的简化下,我们先来好好分析只有两层的汉诺塔问题,显然最简单的走法是:
将a上面的小环移动到b(中间柱)上,再将a下面的大环移动到c(目标柱)上,然后把b上的小环移动到大环上。
总共三步;
3.将这个情况进行推广:
在2的情况下,我们第一步需要将a上面的环移动到b上面,假如最开始有n个环,第一次是a->c的n环汉诺塔问题,第二次呢?将n-1个环移动到b上面,是不是可以写成a->b的n-1环汉诺塔问题,注意了,此时c柱上面没有环,完全可以充当中间柱,然后呢?a->b的n-1环汉诺塔问题的第一步,是不是a->c的n-2次汉诺塔问题?我们似乎找到规律了
4.转换成代码的形式:
void hannoi(n,a,b,c)//按照:最大环数,开始柱子,中间柱子,最后柱子排列.
{
hannoi(n - 1, a, c, b);
}
那么这就是必不可少的一部分,但是我们只是做了第一步呀,还要干什么?没错,我们把上面的环拿了下来到了中间柱子上面,接下来是不是移动最下面的大环?所以我们编写一个叫做move的函数表示移动了大环
void Move(int n, char a, char c)
{
printf("%d from %c to %c\n", n, a, c);
}
这就完成了第一次大环移动了,然后我们需要怎么样?上面的环都到了b(上一阶段的中间柱)上,这个时候是不是重复一次上面的过程,把a当作中间柱子,再移动一次最下面的环?
void Han(int n, char x, char y, char z)
{
if(n==1)
Move(1, x, z);
else
{
Han(n - 1, x, z, y);
Move(n, x, z);
Han(n - 1, y, x, z);
}
}
如图,最后设置退出递归的条件,就是只剩下一块板子的时候。
在一次次简化下,一道题目就这样解决了(虽然仍然有一定的思考维度)
谢谢大家!完整代码如下:
#include <stdio.h>
#include <string.h>
#define N 3
void Han(int n, char a, char b, char c);
void Move(int n, char src, char dst);
void main()
{
Han(N, 'A', 'B', 'C');
}
void Han(int n, char a, char b, char c)
{
if(n==1)
Move(1, a, c);
else
{
Han(n - 1, a, c, b);
Move(n, a, c);
Han(n - 1, b, a, c);
}
}
void Move(int n, char a, char c)
{
printf("%d from %c to %c\n", n, a, c);
}