【C语言】递归实战——汉诺塔详解

请添加图片描述
主页链接: LSR的主页
专栏链接: 《C语言》


前言

关于递归知识的实践与掌握,以汉诺塔的形式展现,希望可以帮助到你。


一、汉诺塔问题介绍

汉诺塔(Tower of Hanoi)是一个经典的递归问题,起源于印度古老传说。问题描述如下:

有 3 根柱子(通常标记为 A、B、C)和 n 个大小不同的圆盘,所有圆盘初始时按 “从小到大” 的顺序堆叠在 A 柱上(最小的在顶端,最大的在底端)。
目标是将所有圆盘从 A 柱移动到 C 柱,移动过程中需遵守两个规则:
每次只能移动 1 个圆盘;
任何时候,大盘子不能放在小盘子上面。


二、解决思路(递归思想)

1.终止条件

当只有 1 个圆盘(n=1)时,直接将其从 A 柱移到 C 柱即可。

2.递归步骤

对于 n 个圆盘,可分解为 3 步:

  1. 先将 A 柱上的前 n-1 个圆盘借助 C 柱移动到 B 柱;
  2. 再将 A 柱上剩下的第 n 个圆盘(最大的那个)直接移动到 C 柱;
  3. 最后将 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 个圆盘的移动。


评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值