362,汉诺塔

想了解更多数据结构以及算法题,可以关注微信公众号“数据结构和算法”,每天一题为你精彩解答。也可以扫描下面的二维码关注
在这里插入图片描述

关于汉诺塔的传说

汉诺塔:汉诺塔(又称河内塔)问题是源于印度一个古老传说的益智玩具。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。

汉诺塔还有另外一个版本

汉诺塔(Towers of Hanoi)是法国人M.Claus(Lucas)于1883年从泰国带至法国的,河内为越战时北越的首都,即现在的胡志明市;1883年法国数学家 Edouard Lucas曾提及这个故事,据说创世纪时Benares有一座波罗教塔,是由三支钻石棒( Pag)所支撑,开始时神在第一根棒上放置64个由上至下依由小至大排列的金盘(Disc) ,并命令僧侣将所有的金盘从第一根石棒移至第三根石棒,且搬运过程中遵守大盘子在小盘子之下的原则,若每日仅搬一个盘子,则当盘子全数搬运完毕之时,此塔将毁损, 而也就是世界末日来临之时

在这里插入图片描述
我们这里不去追本溯源,追究他哪个版本是正确的,我们知道有这样一道题就行,直接文字叙述可能不太直观,我们画个图来分析一下

1,当只有一个的时候
在这里插入图片描述

2,当只有2个的时候
在这里插入图片描述
在这里插入图片描述

3,当只有3个的时候

当n等于3的时候移动的步数就比较多了,我们就不再画图了,我们来看一个动画
在这里插入图片描述

4,当只有4个的时候

在这里插入图片描述

5,当有n个的时候
在这里插入图片描述
在这里插入图片描述
这里主要还是考察对递归的理解,其实递归我们没必要把每一步都推出来,我们只需要找到他的规律和边界条件就行了,就像前面我们讲青蛙跳台阶相关问题的时候,我们提到了斐波那契数列,我们只需要找到斐波那契的规律f(n)=f(n-1)+f(n-2),和他的边界条件f(0)=f(1)=1我们就可以写出代码了,其他的我们就不用管了。

故事篇
这里我来给大家讲个故事吧,很久很久以前有一个人,名叫“孤独求败”,他和家人住在山下过着与世无争的生活,就这样每天日出而作日落而息。有一天他从山上砍柴回来,突然看到家人遇害,他悲痛欲绝,于是决定给家人报仇,但他武艺不行,而仇人的功夫很高,如果硬拼不但报不了仇,而且还会白白丢了性命,所以他决定先去习武。

那天他来到一座山上,看到一个老和尚,向他说明了缘由,老和尚听闻之后怒火中烧,一脚把门前的一个千斤石狮踢开,想帮孤独求败报仇,但又怕得罪他的仇人导致引火烧身,所以他就对孤独求败说:“此仇只有你自己能报”。

孤独求败:“可我功夫不行,根本报不了仇”。

老和尚;“别急,请跟我来”。

于是老和尚带他来到了一间很破旧的房子里,指着里面的一个满是灰尘的破柜子说;“武功秘籍就锁在这里面,你拿到钥匙打开它,把它拿回去自己修炼,等你功夫达到十级之后就可以找你的仇人报仇了”。

然后找来了他的大徒弟对孤独求败说,这是你的大师兄,钥匙你找他要,我要回去睡觉了。然后大师兄说钥匙锁在我自己的小盒子里了,我小盒子的钥匙在二师兄拿着,你先去找二师兄把我这小盒子的钥匙拿到,我把这个小盒子打开,就可以把锁柜子的钥匙给你了。

于是孤独求败又去找到了二师兄,然后二师兄说,大师兄的钥匙被我锁在了我自己的小盒子里了,我自己的小盒子的钥匙在三师兄拿着……

就这样,孤独求败一直找下去,直到找到最小的100师兄拿到钥匙,然后交给第99师兄,打开第99师兄的小盒子,拿到第98师兄的钥匙,又交给第98师兄……一直到大师兄,然后大师兄拿到钥匙打开自己的小盒子,把藏有武功秘籍的柜子钥匙交给孤独求败,这样孤独求败就拿到了武功秘籍,回家修炼去了……

孤独求败从大师兄开始到拿到武功秘籍的过程其实就是递归

读懂了上面的故事,再来看汉诺塔问题就容易多了,我们来看下代码。

 //表示的是把n个圆盘成功的从A移动到C
 public static void hanoi(int n, char A, char B, char C) {
     if (n == 1) {
         //如果只有一个,直接从A移动到C即可
         System.out.println("从" + A + "移动到" + C);
     } else {
         //表示先把n-1个圆盘成功从A移动到B
         hanoi(n - 1, A, C, B);
         //把第n个圆盘从A移动到C
        System.out.println("从" + A + "移动到" + C);
        //表示把n-1个圆盘再成功从B移动到C
        hanoi(n - 1, B, A, C);
    }
}

我们还可以把每个柱子看作是一个栈,大的在下面,小的在上面,所以我们也可以使用栈来实现

 //stackA 源柱  stackB 借助柱  stackC目的柱
 public static void move(Stack stackA, Stack stackB, Stack stackC, int n) {
     if (n == 1) {
         stackC.push(stackA.pop());
     } else {
         move(stackA, stackC, stackB, n - 1);
         stackC.push(stackA.pop());
         move(stackB, stackA, stackC, n - 1);
     }
}

如果想要统计总共移了多少次,可以使用公式(2^n)-1,代码如下

public static long hanoiCount(int n) {
    return (1L << n) - 1;
}

当n=63的时候,我们得到9223372036854775807,当n=64的时候就已经出现了数字溢出。


在这里插入图片描述

### 汉诺塔问题详解 汉诺塔问题是经典的递归算法案例之一。该问题描述如下:有三根杆子A、B、C,其中A上有若干个不同大小的圆盘按照从大到小依次排列。目标是在遵循特定规则的情况下将所有的圆盘移到另一根指定的杆子上。 #### 规则说明 - 每次只允许移动一个圆盘; - 圆盘可以放置在任意一根杆子上,但是始终要保持大盘不能放在小盘之上; #### C++实现方式一 一种常见的解决方案是通过递归来解决问题: ```cpp #include<iostream> using namespace std; void hanoi(int n, char from_rod, char to_rod, char aux_rod) { if (n == 1){ cout << from_rod << "->" << to_rod << endl; return ; } hanoi(n - 1, from_rod, aux_rod, to_rod); cout << from_rod << "->" << to_rod << endl; hanoi(n - 1, aux_rod, to_rod, from_rod); } int main(){ int num_of_disks; cin >> num_of_disks; hanoi(num_of_disks,'A','C','B'); return 0; } ``` 这段程序定义了一个名为`hanoi()`函数来执行具体的移盘操作,并且当输入给定数量的磁盘数目后会调用此函数完成整个过程[^1]。 #### 计算最小步数的方法 对于含有N层碟片的情况来说,最少需要\(2^{N}-1\) 步才能把它们全部转移到另一个柱子上去。这里给出一段用于计算这个数值的小片段: ```cpp #include <iostream> long long calculateMinSteps(int disksCount); int main() { int numberOfDisks; std::cin >> numberOfDisks; long long minStepsRequired = calculateMinSteps(numberOfDisks); std::cout << "Minimum steps required: " << minStepsRequired << "\n"; } // Function calculates minimum number of moves needed based on the formula. long long calculateMinSteps(int disksCount) { if(disksCount <= 0) throw std::invalid_argument("Number of disks must be positive."); static const auto baseCaseValue = [](){return 1LL;}(); return ((disksCount != 1)?(calculateMinSteps(disksCount - 1)*2 + 1):baseCaseValue); } ``` 上述代码展示了如何利用递推关系式\[f(N)=2*f(N−1)+1\], 来求解所需的最短路径长度[^2]. #### 性能分析 值得注意的是,尽管这种基于递归的方式非常适合理解和教学目的,但在实际应用中其效率并不高——因为每增加一层新的碟片都会使总运算量翻倍。具体而言,时间复杂度达到了O(\(2^n\))级别,在处理较大规模的数据集时可能会遇到性能瓶颈[^3]. 另一种更高效的版本采用了迭代而非递归的方式来模拟同样的逻辑流程,从而减少了栈溢出的风险同时也提高了速度表现。不过这超出了当前讨论范围内的重点内容。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

数据结构和算法

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值