一.什么是汉诺塔?

        汉诺塔,也被称为河内塔(Tower of Hanoi),是一个根据传说形成的数学问题:

有三根杆子A、B、C。A杆上有 N 个 (N>1) 穿孔圆盘,盘的尺寸由下到上依次变小。要求按下列规则将所有圆盘移至 C 杆:

        ①每次只能移动一个圆盘;

        ②大盘不能叠在小盘上面。

可将圆盘临时置于 B 杆,也可将从 A 杆移出的圆盘重新移回 A 杆,但都必须遵循上述两条规则。

问题为:应该以何种方式移动?最少要移动多少次?

二.趣味故事

       最早发明这个问题的人是法国数学家爱德华·卢卡斯。根据他的描述,传说越南河内(地点众说纷纭,还有说法是在印度北部)某间寺院有三根银棒,上串 64 个金盘。寺院里的僧侣依照一个古老的预言,以上述规则移动这些盘子;预言说当这些盘子移动完毕,世界就会灭亡。这个传说叫做梵天寺之塔问题(Tower of Brahma puzzle)。

       不过,一点也不用担心。因为如果传说属实,按照一秒可以移动一个盘子来计算,需要整整5849亿年才能完成。整个宇宙至今不过才137亿年,地球诞生至今也就不过46亿年!

2^{_{}^{64}}\div 60\div 60\div 24\div 365=584942417355

三.算法求解

3.1 基本思想       

       汉诺塔问题的解法思想是递归。假设有 A、B、C 三个塔,A 塔有 N 块盘,目标是把这些盘全部移到 C 塔。那么先把 A 塔顶部的 N−1 块盘移动到 B 塔,再把 A 塔剩下的大盘移到 C,最后把 B 塔的 N−1 块盘移到 C。

       ①总是把N-1个盘子从A起始柱,借助B中转柱,移动到C目的柱。

       ②再把A起始柱最大的盘子移动到C目的柱。

       ③这时,B上面剩下的N-1个盘子,要移动到C柱上。那么B就变为了起始柱,A为中转柱,C为目的柱。

如此递归地使用下去, 就可以求解。

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

void move(char pos1, char pos2)
{
	printf("%c->%c\t", pos1, pos2);
}

void Hanoi(int n, char pos1, char pos2, char pos3)
{
	if (n == 1)
	{
		move(pos1, pos3);
	}
	else
	{
		Hanoi(n - 1, pos1, pos3, pos2);
		move(pos1, pos3);
		Hanoi(n - 1, pos2, pos1, pos3);
	}
}

int main()
{
	Hanoi(3, 'A', 'B', 'C');
	return 0;
}

3.2运行结果

四.时间复杂度

       递归的过程需要在每个阶段进行两次递归调用(分别处理 n-1 个盘子)。因此,总的移动次数为 2^n - 1。这个递归算法的时间复杂度是 O(2^n),随着盘子数量的增加,移动次数呈指数增长。

       这里我只呈现了3个盘子的运行结果,如果是64个盘子,那么程序就会一直跑啊跑,我们来看看计算机移动完这64个盘子需要多久。

       这里是笔者电脑上的处理器,主频2.70GHz。

 2^{_{}^{64}}\div 60\div 60\div 24\div 365\div 2.7\div 1024\div 1024\div 1024\approx 207

可以看到该处理器需要运行207年才可以移动完这64个盘子,大家207年后见,届时评论区公布运行结果!


通过递归解决汉诺塔问题,不仅能帮助我们理解递归算法的基本思想,也能加深对递归函数调用栈的理解。这个问题在编程面试中经常出现,也是学习递归算法的经典范例。

希望你在阅读本文后能够理解递归的基本概念,动手实践实现其他递归问题。如果你有任何问题或者改进建议,欢迎在评论区留言,和大家一起讨论。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐