一个经典的汉诺塔问题,带着我自己的理解给做这个问题的友友们解决一下,包括我本人在做的时候也遇到的一些问题给大家阐述一下。话不多说,来看:
汉诺塔
问题描述:
汉诺塔(Hanoi Tower),又称河内塔,源于印度一个古老传说。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,任何时候,在小圆盘上都不能放大圆盘,且在三根柱子之间一次只能移动一个圆盘。
分析问题:
将a柱上的盘子,借助b柱移动到c柱上
初次接触类似的问题,乍看之下肯定会感觉无从下手,不如先分析一个容易一点的,假设a柱上只有三个盘子
如要实现转移则一共需要7步:
1.先将最上面的盘子移动到c柱上;
2.将中间的盘子移动到b柱;
3.再将c柱上的盘子移动到b柱上;
4.再将a柱上最后一个盘子移动到c柱上;
5.再将b柱上最上面盘子移动到a柱上;
6.再把b柱上的盘子移动到c柱;
7.再将a柱上的盘子移动到c柱上
只有3个盘子看似不难,实际上随着盘子数的增加,需要的步数会沿着2的n次方减1曲线增加;
如果真的要把64个圆盘从a柱子移动到c柱子上,第一步应该怎么做?虽然可以肯定,第一步唯一的选择是移动a最上面的那个圆盘,但是应该将其移到b还是c呢?很难确定。因为接下来的第二步、第三步……直到最后一步,看起来都是很难确定的。能立即确定的是最后一步:最后一步的盘子肯定也是a最上面那个圆盘,并且是由a或b移动到c——此前已经将63个圆盘移动到了c上。
也许你会说,管他呢,先随便试着移动一下好了。如果你这么做,你会发现,接下来你会面临越来越多类似的选择,对每一个选择都“试”一下的话,你会偏离正确的道路越来越远,直到你发现你接下来无法进行为止。
如果将这个问题的盘子数量减为10个或更少,就不会有太大的问题了。但盘子数量为64的话,你一共需要移动约1800亿亿步(18,446,744,073,709,551,615),才能最终完成整个过程。这是一个天文数字,没有人能够在有生之年通过手动的方式来完成它。即使借助于计算机,假设计算机每秒能够移动100万步,那么约需要18万亿秒,即58万年。将计算机的速度再提高1000倍,即每秒10亿步,也需要584年才能够完成。
解决问题:
当我看到这个问题时我脑子里想了3个盘子的情况和4个盘子的情况,当有4个盘子的时候需要15步这时候光想就已经混乱了,我借助了画图,同时也发现了不管是最初a柱上有几个盘子在移动过程中都有一个共同点就是将除了a柱最下面的一个盘子以外的盘子全部移动到b柱上
所以这么复杂的过程,我们可以拆解一下
假设a柱上有n个盘子,如要实现移动则需要
1.把a柱n-1个盘子借助c柱移动到b柱
2.再把a柱上第n个盘子移动到c柱
3.把b柱上的n-2个盘子借助c柱移动到a柱上
4.再把b柱上最后一个盘子移动到c柱上
.以此类推>>>直到全部移动到c柱上
看到这我想聪明的友友你们已经发现规律了,即每次都是先将其他圆盘移动到辅助柱子上,并将最底下的圆盘移到c柱子上,然后再把原先的柱子作为辅助柱子,并重复此过程。
这个过程称为递归,即定义一组基本操作,这组操作将规模小一点(或大一点)的操作当做一个整体——无需关心它的细节,只当它已经完成了——然后执行剩下的操作。而在更小或更大的规模中也依此操作,直到规模达到预定值
因此,可以用递归的方法来解决这一复杂的问题
我们假设函数Hanoi(n, t1,t2, t3)用于将n个圆盘由t1移动到t3,t2作为辅助柱子。那么我们可以这样实现这个递归过程:
void Hanoi(int x,char t1,char t2,char t3)
{
if (x == 1)
{
move(x, t1, t3);//(move函数是移动最下面的一个盘子)
}
else
{
Hanoi(x - 1, t1, t3, t2);//1柱x-1个盘子移动到2柱,3柱为辅助柱(注意参数顺序)
move(x, t1, t3);//第x个盘子从1柱移动到3柱
Hanoi(x - 1, t2, t1, t3);//将x-1个盘子从2移动到3,1柱为辅助柱
}
}