一、汉罗塔问题
1.介绍
汉诺塔问题是一个经典的问题。汉诺塔(Hanoi Tower),又称河内塔,源于印度一个古老传说。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把所有圆盘移到另一根柱子上。并且规定,任何时候,在小圆盘上都不能放大圆盘,且一次只能搬动一个圆盘。问应该如何操作搬动次数最少?
2.讲解
假设我们有A、B、C三根石柱,其中A为起始石柱,B为中转石柱,C为目的石柱。
如果A上有一个圆盘,我们可以直接将A上圆盘移到C上,即A->C
,共1步;
如果A上有两个圆盘,我们需要借助中转石柱,即B石柱,先将A上小圆盘移到B上,再将A上大圆盘移到C上,最后将之前移到B上的小圆盘移到C上,即A->B A->C B->C
,共3步;
如果A上有三个圆盘,我们首先将小圆盘移到C上,接着中圆盘移到B后小圆盘再次移到B,然后大圆盘移到C,接着小圆盘移到A后中圆盘移到C,最后小圆盘移到C,即A->C A->B C->B A->C B->A B->C A->C
,共7步。
…
以此类推,最小移动次数总为2^n-1(n为圆盘总个数)。
需注意,为取最小移动次数,在实际移动时,当圆盘总数为偶数时,我们将第一个圆盘,即最小圆盘先移到B,当圆盘总数为奇数时,则先移到C。
二、编程中的汉罗塔问题
现在我们想要编写一个程序,输入n
值,代表起始石柱上圆盘总数,然后让程序打印出移动步骤,来帮助我们完成搬运工作。
1.代码概览
#include<stdio.h>
Move(char pos1, char pos3)//打印移动步骤
{
printf("%c->%c ", pos1, pos3);
}
Hanio(int n, char pos1, char pos2, char pos3)//pos1起始石柱,pos2中转石柱,pos3目的石柱
{
if (n == 1)
{
Move(pos1, pos3);
}
else
{
Hanio(n - 1, pos1, pos3, pos2);//交换pos2,pos3
Move(pos1, pos3);
Hanio(n - 1, pos2, pos1, pos3);//交换pos1,pos2
}
}
int main()
{
int n = 0;
scanf("%d", &n);
Hanio(n, 'A', 'B', 'C');
return 0;
}
2.详解代码
我们来看看这段代码是如何实现的,先来看一张动图(以4个圆盘为例)
上图可以分为三个阶段
- 借
pos3
,将pos1
最上面3个圆盘转移到pos2
; - 交换
pos1
、pos3
,将最大的圆盘从pos1
移至pos3
; - 借
pos1
,将pos2
上3个圆盘移至pos3
上。
其实到此,我们已经说出了这段代码的精髓了。
上面我们提到:
1个圆盘是A->C
2个圆盘是A->B | A->C | B->C
3个圆盘是A->C A->B C->B | A->C | B->A B->C A->C
4个圆盘是A->B A->C B->C A->B C->A C->B A->B | A->C | B->C B->A C->A B->C A->B A->C B->C
…
易知,除了1个圆盘外,我们都可以用|划分出这三个阶段。
1 Hanio(int n, char pos1, char pos2, char pos3)
2 {
3 if (n == 1)
4 {
5 Move(pos1, pos3);
6 }
7 else
8 {
9 Hanio(n - 1, pos1, pos3, pos2);
10 Move(pos1, pos3);
11 Hanio(n - 1, pos2, pos1, pos3);
12 }
13 }
所以我们构造分支,如果n=1,则直接将圆盘从A搬到C,如果n>1,则else
,第9行为第一阶段,第10行为第二阶段,第11行为第三阶段。
以n=3为例,运行逻辑如下:
所谓大事化小事,这是递归的意义所在,可以化繁为简。而图片中的’小事‘就是前文的三个阶段,即第一层递归的三个支线,其中的两个Hanio
支线又可以细分、往复下去。可以发现一开始,从外到内,Hanio
函数一直都是在传递形参,即‘递’ ,直到n=1时,才进入‘归’,这时这种循环往复从内到外继续进行。再深思,这种循环往复归根其实是pos1
、pos2
、pos3
的循环往复。
建议大家对着运行逻辑图,拿纸笔将每一次交换都按顺序写下来,用心领悟,试着去分析一下,个例搞透了,整体思想也就容易理解了,最终递归,我们也就拿捏了。
其实简单地讲,递归无非就是一个从外‘递’至内,从内‘归’回外的过程,我们需要的只是明确递的是什么,归的是什么,这样就很容易理解汉罗塔问题了。
到此本篇就结束了,有什么不懂的可以在评论区一起交流探讨,学习与成长的道路上有个同伴总是不错的。