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;

}

总结:

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

相关推荐
Bona Sun2 小时前
单片机手搓掌上游戏机(十一)—esp8266运行gameboy模拟器之硬件连接
c语言·c++·单片机·游戏机
酸钠鈀2 小时前
模拟IIC通讯 基于状态机
c语言
爪哇部落算法小助手2 小时前
每日两题day50
数据结构·c++·算法
curry____3033 小时前
基本算法(2025.11.21)
c++·算法
WWZZ20254 小时前
快速上手大模型:深度学习5(实践:过、欠拟合)
人工智能·深度学习·神经网络·算法·机器人·大模型·具身智能
司铭鸿4 小时前
图论中的协同寻径:如何找到最小带权子图实现双源共达?
linux·前端·数据结构·数据库·算法·图论
橘子真甜~4 小时前
C/C++ Linux网络编程6 - poll解决客户端并发连接问题
服务器·c语言·开发语言·网络·c++·poll
小年糕是糕手6 小时前
【C++】C++入门 -- 输入&输出、缺省参数
c语言·开发语言·数据结构·c++·算法·leetcode·排序算法
情怀姑娘6 小时前
面试题---------------场景+算法
java·算法·mybatis