一.起源:
汉诺塔(又称河内塔)问题是源于印度一个古老传说的益智玩具。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。
二.抽象为数学问题:
如下图所示,从左到右有A、B、C三根柱子,其中A柱子上面有从小叠到大的n个圆盘,现要求将A柱子上的圆盘移到C柱子上去,期间只有一个原则:一次只能移到一个盘子且大盘子不能在小盘子上面,求移动的步骤和移动的次数。
解:
(1)n = 1时
第1次 1号盘 A---->C sum = 1 次
(2) n = 2时
第1次 1号盘 A---->B
第2次 2号盘 A---->C
第3次 1号盘 B---->C sum = 3 次
(3)n = 3时
第1次 1号盘 A---->C
第2次 2号盘 A---->B
第3次 1号盘 C---->B
第4次 3号盘 A---->C
第5次 1号盘 B---->A
第6次 2号盘 B---->C
第7次 1号盘 A---->C sum = 7 次
发现规律:1个圆盘的次数 2的1次方减1
2个圆盘的次数 2的2次方减1
3个圆盘的次数 2的3次方减1
。 。 。 。 。
n个圆盘的次数 2的n次方减1
故:移动次数为:2^n - 1
三.算法分析(递归算法):
在利用计算机求汉诺塔问题时,必不可少的一步是对整个实现求解进行算法分析。到目前为止,求解汉诺塔问题最简单的算法通过递归来求,至于什么是递归,递归实现的机制是什么,简单点就是自己是一个方法或者是函数,但是在自己这个函数里有调用自己这个函数的语句,而这个调用怎么才能调用结束呢?,这里还必须有一个结束点,或者具体的说是在调用到某一次后函数能返回一个确定的值,接着倒数第二个就能返回一个确定的值,一直到第一次调用的这个函数能返回一个确定的值。
实现这个算法简单分为三个步骤:
(1)把n-1个盘子由A 移到 B
(2)把第n个盘子由 A移到 C
(3)把n-1个盘子由B 移到 C
不难发现,移到的步数必定为奇数步:
(1)中间的一步是把最大的一个盘子由A移到C上去
(2)中间一步之上可以看成把A上n-1个盘子通过借助辅助塔(C塔)移到了B上
(3)中间一步之下可以看成把B上n-1个盘子通过借助辅助塔(A塔)移到了C上
四.算法实现
/**
* 实验题目:
* 采用递归算法求解汉诺塔问题
* 实验目的:
* 领会基本递归算法设计
* 实验内容:
* 采用递归方法求解汉诺塔问题,输出3个盘片的移动过程
*/
#include <stdio.h>
/*--------------------递归算法----------------------*/
/**
* 汉诺塔递归算法
* @param a: 起始柱子
* @param b: 辅助柱子
* @param c: 目标柱子
*/
static void hanoi(int n, char a, char b, char c)
{
if(n == 1)
printf("\t将第%d个盘片从%c移动到%c\n", n, a, c);
else
{
// 把A上n-1个盘子通过借助辅助塔(C塔)移到了B上
hanoi(n - 1, a, c, b);
printf("\t将第%d个盘片从%c移动到%c\n", n, a, c);
// 把B上n-1个盘子通过借助辅助塔(A塔)移到了C上
hanoi(n - 1, b, a, c);
}
}
int main(int argc, char *argv[])
{
int n = 3;
printf("递归算法: %d个盘片移动过程:\n", n);
hanoi(n, 'A', 'B', 'C');
return 0;
}
测试结果:
递归算法: 3个盘片移动过程:
将第1个盘片从A移动到C
将第2个盘片从A移动到B
将第1个盘片从C移动到B
将第3个盘片从A移动到C
将第1个盘片从B移动到A
将第2个盘片从B移动到C
将第1个盘片从A移动到C