Tanoi汉诺塔最详细的一篇讲解
这是我写的非常详细一篇的关于递归经典汉诺塔问题,相信如果你有耐心看完,相信绝对会对这个想破脑袋也不知道到底咋套娃的问题会有一个全新的认识,好了,废话不多说,我们开始吧。
首先介绍一下什么是汉诺塔问题:
一块板上有三根针 A、B、C。A 针上套有 64 个大小不等的圆盘,按照大的在下、小的在上的顺序排列,要把这 64 个圆盘从 A 针移动到 C 针上,每次只能移动一个圆盘,移动过程可以借助 B 针。但在任何时候,任何针上的圆盘都必须保持大盘在下,小盘在上。求出移动的步骤。^
如何用算法来实现这个问题
解决汉诺塔一共的步骤就三个,而这三个步骤将是贯彻汉诺塔问题的核心,所以要仔细理解这三个步骤,理解之后才会对汉诺塔这个问题解决有一个根本的认识。
首先看看的我们的起始状态
接下来就是关键的核心步骤!!!
三个核心步骤
1.将a上的(n-1)个盘子通过c移动到b
2.将a上的最后一个盘子移动到c
3.将b上的(n-1)个盘子移动到c
在这里,我们再先提出三个小概念,以方便我们以下的理解:起始柱、中间柱、目标柱。
这三个概念从字面上理解应该不难,但是在这个题目中,a是起始柱,b是中间柱,c是目标柱吗?答案肯定是错误的!!!这三个概念是相对的,而不是绝对的。就比如在步骤1中,a上的(n-1)盘子通过c移动到b,那么a是起始柱,b是目标柱,c是中间柱。
汉诺塔的代码实现
接下来我们根据我们上面的讲解,来用代码进行对汉诺塔问题的实现吧。
首先我们将核心算法用代码给大家展现出来
void hanoi(int n, char x, char y, char z)
{
if (n == 1)
{
move(x, z);
}
else
{
hanoi(n - 1, x, z, y);
move(x, z);
hanoi(n - 1, y, x, z);
}
}
void move(char c1, char c2)
{
printf("%c-->%c\n", c1, c2);
}
可能很多小伙伴看到这里一脸懵逼,没关系,我来慢慢介绍一下这些参数的意思。首先我们自定义一个函数hanoi,这个函数的形参代表的意思是有n个盘子在x柱子(起始柱)上,我们通过y柱子(中间柱)最后移动z柱子(目标柱)上。
那么当n=1时,我们只需要一个步骤,即是将x柱子上的一个盘子直接移动到z上,这里我们再定义一个函数move来实现这个功能。
相信到这里大家理解起来应该没什么问题,那么n如果大于2呢,那么这里我们将再次调用hanoi这个函数,即将x柱子上(n-1)个盘子经过z柱子移动到y,再将x柱子上的最后一个盘子移动到z,再将y柱子上面的(n-1)个盘子经过x柱子再移动的z上。这里的步骤就是我上面写的核心思想,没有理解的可以返回上面再看看。
就是这样一次一次调用直到(n-1)等于1时,我们的嵌套调才会停止,这时调用会再一次一次返回,最终来解决这个问题。
估计看到这里你还是一脸懵,没关系,我们举个例子你就应该可以有一个更好的理解。
在这里我们举一个n=3的例子。
可能大家刚刚看到这个图一脸懵逼,没关系,接下来我们来一步一步解析。
程序执行的步骤我已经标注了,大家按上面的步骤一步一步往下看即可。
首先,我们输入盘子的个数三个,并且命名了三个柱子的名字a,b,c,一开始n不等于1,执行else里面的第一句语句,接着进入第二个模块,此时hanoi(2,‘a’,‘c’,‘b’),即将a上的两个盘子通过c移动到b,接着的步骤和前面的一样,判断n不等于1,执行else里面的第一个语句,进入第三个模块,此时hanoi(1,‘a’,‘b’,‘c’),n为1,则进入move函数,打印a–>c,执行完以后,我们将返回第二个模块接着往下执行,这时来到步骤(7.),碰到move函数,打印a–>b,执行完以后继续往下执行,这时来到第四个模块,此时hanoi(1,‘c’,‘a’,‘b’),再次碰到move函数,打印c–>b,打印结束返回,此时第二个模块执行完毕,再次返回到第一个模块,即步骤(11.),此时又碰到move函数,接着打印a–>c,接着往下执行,来到第五个模块,此时hanoi(2,‘b’,‘a’,‘c’),此时判断n不等于1,执行else里面的第一个语句,来到第六个模块,此时hanoi(1,‘b’,‘c’,‘a’),n为1,进入move函数,打印b–>a,打印完以后返回第五个模块,碰到move函数,打印b–>c,打印完以后继续往下执行,即步骤(16.),来到第七个模块,此时hanoi(1,‘a’,‘b’,‘c’),n为1,执行move函数,打印a–>c,打印完以后返回到第五个模块,但五个模块已经执行完毕,则返回到第一个模块,第一个模块也已经执行完毕,所以整个函数执行完毕。
可能大家看完上面我写的步骤还是一知半解的,这时你需要根据我上面写的自己拿笔画出一个执行导图,自己捋清楚执行顺序,才能将这个问题完全弄懂。
如果用统计执行步数,我们可以定义一个全局变量count,然后在每个move函数里面执行一个count++,最后在主函数里输出,则可以统计执行步数。
好啦,我们将完整代码呈现出来:
#include <stdio.h>
int count = 0;
void move(char c1, char c2)
{
count++;
printf("%c-->%c\n", c1, c2);
}
void hanoi(int n, char x, char y, char z)
{
if (n == 1)
{
move(x, z);
}
else
{
hanoi(n - 1, x, z, y);
move(x, z);
hanoi(n - 1, y, x, z);
}
}
int main()
{
int N = 0;
printf("Please input=");
scanf("%d", &N);
hanoi(N,'a','b','c');
printf("执行的次数为:%d", count);
return 0;
}