主页链接: LSR的主页
专栏链接: 《C语言》
前言
关于递归知识的实践与掌握,以汉诺塔的形式展现,希望可以帮助到你。
一、汉诺塔问题介绍
汉诺塔(Tower of Hanoi)是一个经典的递归问题,起源于印度古老传说。问题描述如下:
有 3 根柱子(通常标记为 A、B、C)和 n 个大小不同的圆盘,所有圆盘初始时按 “从小到大” 的顺序堆叠在 A 柱上(最小的在顶端,最大的在底端)。
目标是将所有圆盘从 A 柱移动到 C 柱,移动过程中需遵守两个规则:
每次只能移动 1 个圆盘;
任何时候,大盘子不能放在小盘子上面。
二、解决思路(递归思想)
1.终止条件
当只有 1 个圆盘(n=1)时,直接将其从 A 柱移到 C 柱即可。
2.递归步骤
对于 n 个圆盘,可分解为 3 步:
- 先将 A 柱上的前 n-1 个圆盘借助 C 柱移动到 B 柱;
- 再将 A 柱上剩下的第 n 个圆盘(最大的那个)直接移动到 C 柱;
- 最后将 B 柱上的 n-1 个圆盘借助 A 柱移动到 C 柱。
通过这种递归分解,大问题被逐步简化为小问题,最终可完成所有圆盘的移动。
当只有 1 个圆盘时,问题极其简单:
直接将圆盘从源柱(A) 移动到目标柱(C) 即可,无需辅助柱(B)。
这是递归的终止条件—— 当问题简化到无法再分解时,直接给出解决方案。
假设有 2 个圆盘(1 号:小圆盘;2 号:大圆盘):初始都在 A 柱,目标是移到 C 柱,B 为辅助柱。
此时问题可分解为 3 步,这 3 步正是 n=2 时递归逻辑的体现:
第一步:先将上面的 1 个圆盘(1 号)从 A 柱移到辅助柱 B(此时 B 临时作为目标柱,C 作为辅助)。
目的:腾出 A 柱最下面的大圆盘(2 号),让它能直接移到 C 柱。
第二步:将 A 柱剩下的大圆盘(2 号)直接从 A 移到目标柱 C。
这是最关键的一步 —— 因为此时 A 柱只有 2 号盘,且 C 柱为空,符合 “大盘在下” 的规则。
第三步:将临时放在 B 柱的 1 号圆盘,从 B 移到目标柱 C(此时 A 作为辅助柱)。
目的:让 1 号盘(小)放在 2 号盘(大)上面,完成所有圆盘的移动。
当有 3 个圆盘(1 号最小,3 号最大)时,问题更复杂,但分解思路与 n=2 一致 —— 先处理 “上面的 n-1 个圆盘”,再处理 “最大的圆盘”。
具体步骤可分解为 3 个阶段,每个阶段都是对 “n-1 个圆盘” 问题的复用:
阶段 1:将 A 柱的前 2 个圆盘(1 号、2 号)移到 B 柱(用 C 柱作为辅助)
这本质上是一个 “n=2” 的子问题:源柱是 A,目标柱是 B,辅助柱是 C。
移动过程与 n=2 时完全一致,只是目标柱从 C 换成了 B:
A→C(1 号) → A→B(2 号) → C→B(1 号)。
此时状态:A 柱只剩 3 号盘,B 柱有 1 号、2 号盘(1 号在上),C 柱空。
阶段 2:将 A 柱的最大圆盘(3 号)直接移到 C 柱
这是 “n=3” 问题的关键一步 —— 因为此时 A 柱只有 3 号盘,C 柱为空,且 3 号是最大的盘,移动后 C 柱底部是最大盘,符合规则。
移动:A→C(3 号)。
此时状态:A 柱空,B 柱有 1 号、2 号盘,C 柱有 3 号盘。
阶段 3:将 B 柱的 2 个圆盘(1 号、2 号)移到 C 柱(用 A 柱作为辅助)
这又是一个 “n=2” 的子问题:源柱是 B,目标柱是 C,辅助柱是 A。
移动过程同样复用 n=2 的逻辑:
B→A(1 号) → B→C(2 号) → A→C(1 号)。
此时状态:所有圆盘(1 号、2 号、3 号)按规则堆叠在 C 柱,完成任务。
3.最少移动次数
n 个圆盘的最少移动次数为
2 ^n −1(例如 3 个圆盘需要 7 次)。
n = 3 动图如下:

n = 4 动图如下:

三、通用逻辑:n个圆盘递归思想
从 n=1、n=2、n=3 的例子中,我们可以总结出通用的递归思路:
对于任意 n 个圆盘,要从源柱(from) 移到目标柱(to),借助辅助柱(temp),步骤为:
递归处理子问题 1:将 from 柱上的前 n-1 个圆盘,借助 to 柱移到 temp 柱。
(此时 temp 是 “临时目标柱”,to 是 “辅助柱”)
直接移动最大盘:将 from 柱上剩下的第 n 个圆盘(最大的那个)直接移到 to 柱。
(此时 from 柱只剩最大盘,to 柱可直接接收,无违反规则的风险)
递归处理子问题 2:将 temp 柱上的 n-1 个圆盘,借助 from 柱移到 to 柱。
(此时 temp 是 “源柱”,from 是 “辅助柱”,to 是最终目标柱)。
四、代码实现
#include <stdio.h>
/**
* 汉诺塔递归函数
* @param n:圆盘数量
* @param from:源柱子(初始存放圆盘的柱子)
* @param temp:辅助柱子(临时存放圆盘的柱子)
* @param to:目标柱子(最终要移动到的柱子)
*/
void hanoi(int n, char from, char temp, char to) {
// 终止条件:只有1个圆盘时,直接从源柱移到目标柱
if (n == 1) {
printf("将圆盘 %d 从 %c 移动到 %c\n", n, from, to);
return;
}
// 步骤1:将n-1个圆盘从from移到temp(用to作为辅助)
hanoi(n - 1, from, to, temp);
// 步骤2:将第n个圆盘从from移到to
printf("将圆盘 %d 从 %c 移动到 %c\n", n, from, to);
// 步骤3:将n-1个圆盘从temp移到to(用from作为辅助)
hanoi(n - 1, temp, from, to);
}
int main() {
int n;
printf("请输入汉诺塔的圆盘数量:");
scanf("%d", &n);
// 调用递归函数,初始柱子为A,辅助为B,目标为C
hanoi(n, 'A', 'B', 'C');
return 0;
}
结果如下:
请输入汉诺塔的圆盘数量:3
将圆盘 1 从 A 移动到 C
将圆盘 2 从 A 移动到 B
将圆盘 1 从 C 移动到 B
将圆盘 3 从 A 移动到 C
将圆盘 1 从 B 移动到 A
将圆盘 2 从 B 移动到 C
将圆盘 1 从 A 移动到 C
总结
汉诺塔每一步的思想都遵循 “分解 - 解决 - 合并” 的递归逻辑:
分解:将 n 个圆盘的问题分解为两个 n-1 个圆盘的子问题和一个直接移动最大盘的步骤;
解决:通过递归解决 n-1 个圆盘的子问题(直到 n=1 时直接移动);
合并:将子问题的解(n-1 个圆盘的移动)与最大盘的移动结合,完成 n 个圆盘的移动。
2万+

被折叠的 条评论
为什么被折叠?



