C语言经典算法:汉诺塔问题

引言:

这是最近接触的一个新算法,历时多天终于明白其中的道理,我在这里分享一点个人的感悟,希望对大家有所帮助。

背景:

问题起源

发明者:法国数学家爱德华·卢卡斯(Édouard Lucas)于1883年发明

传说背景:源于一个古老的印度传说,描述在贝拿勒斯(现瓦拉纳西)的寺庙中有三根钻石柱子,上帝创造世界时在第一根柱子上放置了64个金盘,要求僧侣将所有盘子移到第三根柱子上

问题规则

三根柱子:通常标记为 A(起始柱)、B(辅助柱)、C(目标柱)

盘子特性:有 n 个不同大小的盘子,大盘不能放在小盘上面

移动限制:每次只能移动一个盘子,且只能移动到相邻的柱子上

目标:将所有盘子从起始柱 A 移动到目标柱 C

数学模型

汉诺塔属于典型的递归问题,但是递归问题又是什么呢?

递归定义

递归是一种编程技术,指函数直接或间接地调用自身来解决问题的方法。

(个人理解就是不断向前推,直到推到能用具体情况计算位置)不保证理解正确
递归的核心要素

基础情况(Base Case):递归的终止条件,防止无限循环

递归情况(Recursive Case):函数调用自身的部分,通常处理规模更小的子问题
递归的特点

自我调用:函数在执行过程中会调用自身

问题分解:将复杂问题分解为相同类型的更小子问题

规模缩减:每次递归调用都使问题规模变小

栈结构:系统使用调用栈来管理递归过程中的函数调用
递归的应用场景

数学计算:阶乘、斐波那契数列等

数据结构遍历:树、图的深度优先搜索

分治算法:快速排序、归并排序

经典问题:汉诺塔、八皇后等

正文:

大多数人的困惑其实是不理解其内部的移动逻辑,从而无法看懂抽象的函数。所以我以4层汉诺塔为例,详细解释它的运行规律。

目的:将A柱上的四层盘子移到C柱。

因为汉诺塔上小下大放置的规则,下图是此算法的灵魂所在。

为什么这是灵魂所在呢?其实这就牵扯到递归的思想了。

我们不妨先假设上3层为一个整体Q,要想把序号4的盘子移到目标柱C,需要先把Q移到辅助柱B,再将序号4的盘子移动到目标柱C。(此步即将问题简化为两层,也是递归最终的位置)

而接下来我们需将Q从起始柱B移动到目标柱C。(完成最终的移动)

这就是一个新的汉诺塔问题了,假设上两层为一个整体Q2,要想把序号3的盘子起始柱B移到目标柱C,需要先把Q移辅助柱A,再将序号3的盘子移动到目标柱C。(注意起始柱、辅助柱、目标柱的调换)

然后是将Q2(上两层)从起始柱A移动到目标柱C,这就需将序号1的盘子先移动到辅助B,再将序号2的盘子直接移动到目标柱C。

最后是将序号1的盘子直接移动到目标柱C。(强调这句是因为其不符合递归规律,属于条件判断的特例)

通过上述过程,我们不难发现此问题的灵魂在于把前n-1层看为一个整体,将复杂的问题转化为简单的两层问题,继而不断向前推。而我们只需加一个判断条件(n==1),使递归不断执行到最后两层为止。

这就是汉诺塔核心函数的含义:

函数hanoi()括号中的第一个位置表示要移动的数量,第二个位置表示起始柱,第三个位置表示辅助柱,第四个位置表示目标柱

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);//将第n层的盘子直接从pos1移动到pos3

hanoi(n - 1, pos2, pos1, pos3);

}

}

else{}中的代码作用即将前n-1层看成一个整体来执行新的汉诺塔问题(调换三种柱子的位置)

以下是汉诺塔问题的完整代码和4层汉诺塔操作步骤:

#include<stdio.h>

/**

* 移动盘子函数

* @param pos1 起始位置

* @param pos3 目标位置

*/

void move(char pos1, char pos3)

{

printf(" %c->%c ", pos1, pos3);

}

/**

* 汉诺塔递归解决函数

* @param n 盘子数量

* @param pos1 起始柱

* @param pos2 辅助柱

* @param pos3 目标柱

*/

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()

{

int n;

printf("请输入汉诺塔的盘子数: ");

scanf("%d", &n);

printf("移动步骤:\n");

hanoi(n, 'A', 'B', 'C');

printf("\n完成!\n");

return 0;

}

总结:

以上就是我的所有感悟了,感谢你的阅读,希望对你有所帮助。因为我也是刚接触此问题,所以难免有错误和疏漏的地方,欢迎大家的指正和批评。

相关推荐
ytttr87315 分钟前
matlab实现多标签K近邻(ML-KNN)算法
算法·机器学习·matlab
一招定胜负22 分钟前
逻辑回归调优三板斧:参数调整、阈值设定、数据集平衡
算法·机器学习·逻辑回归
豆约翰25 分钟前
Z字形扫描ccf
java·开发语言·算法
Salt_072828 分钟前
DAY 35 文件的规范拆分和写法
python·算法·机器学习
小尧嵌入式30 分钟前
C语言中的面向对象思想
c语言·开发语言·数据结构·c++·单片机·qt
风筝在晴天搁浅34 分钟前
代码随想录 109.冗余连接Ⅱ
算法
业精于勤的牙34 分钟前
浅谈:算法中的斐波那契数(三)
算法·职场和发展
一杯美式 no sugar37 分钟前
数据结构——单向无头不循环链表
c语言·数据结构·链表
ss27339 分钟前
阻塞队列:三组核心方法全对比
java·数据结构·算法
小O的算法实验室40 分钟前
2026年SEVC SCI2区,面向空地跨域无人集群的目标引导自适应路径规划方法,深度解析+性能实测
算法·论文复现·智能算法·智能算法改进