跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

汉诺塔问题的递归与非递归 C++ 解法

汉诺塔是经典的递归入门问题。递归解法把 n 个圆盘拆成 n-1 子问题,直接写代码很容易。非递归解法要么手动用栈模拟调用,要么利用交替移动最小的圆盘和合法移动的规律,能避免递归过深栈溢出。C++ 代码分别展示了这两种方式,其中非递归的栈模拟思想可推广到其他递归场景。

灵魂摆渡发布于 2026/6/300 浏览
汉诺塔问题的递归与非递归 C++ 解法

汉诺塔是一个经典的递归算法入门问题。规则很简单:有三根柱子 A、B、C,n 个大小不同的圆盘初始都按由小到大的顺序叠在 A 上。要求把所有圆盘移动到 C 上,过程中可以借助 B,但每次只能移动最上面的一个圆盘,而且任何时刻都不能出现大盘压小盘。

最少移动次数是 2^n - 1。当 n=1,一步搞定;n=2,需要 3 步;n=3 就需要 7 步,以此类推。递归解法的核心是把 n 个圆盘的问题拆成三个子问题:先把上面 n-1 个从 A 移到 B(以 C 为辅助),然后把最大的那个从 A 移到 C,最后再把 B 上的 n-1 个移到 C(以 A 为辅助)。当 n 减到 1 时,就可以直接移动了。

递归实现

递归代码很直观,直接照搬分解思路:

#include <iostream>
using namespace std;

int n;

void hanoi(char a, char b, char c, int n) {
    if (n > 0) {
        hanoi(a, c, b, n - 1);
        cout << n << " from: " << a << " to " << c << endl;
        hanoi(b, a, c, n - 1);
    }
}

int main() {
    cin >> n;
    char a = 'A', b = 'B', c = 'C';
    hanoi(a, b, c, n);
    return 0;
}

这个函数在 n>0 时,先递归处理上面 n-1 个圆盘,移动完后把当前最大圆盘从 a 移到 c,然后递归把那 n-1 个从 b 移到 c。参数顺序可能会让人迷糊,但对应起来就是:第一次调用把 A 上的 n-1 个移到 B,所以源是 A,目标是 B,辅助是 C;第二次调用把 B 上的 n-1 个移到 C,源是 B,目标是 C,辅助是 A。

非递归解法:用栈模拟或规律循环

递归的优点是代码简洁,但如果圆盘数很大,递归层数过深可能导致栈溢出。非递归实现可以避免这个问题,同时也能帮助我们更好地理解递归的底层机制。

一种常用的非递归方法是手动维护一个栈,模拟系统调用栈。我们可以把每次待处理的任务(盘数、源、辅助、目标)压栈,然后循环处理直到栈空。不过更简单的非递归思路是利用汉诺塔的一个经典规律:交替完成两种移动。

假设圆盘按顺序编号从 1(最小)到 n(最大)。如果 n 是奇数,移动序列有下面规律:

  1. 将圆盘 1 移动到下一个柱子(按 A→C→B→A 循环);
  2. 然后,在剩下的两个柱子之间进行一次合法移动(即只能把较小的盘移到较大的盘上,或移到空柱子上);
  3. 重复以上两步,直到所有圆盘移到 C。

如果 n 是偶数,起点柱子 A 上的第一步移动圆盘 1 要按 A→B→C→A 的顺序。按照这个规律,我们可以在循环中判断何时结束。

下面这段代码用栈来存储每个柱子上的圆盘,并按照上述规律模拟移动:

#include <iostream>
#include <stack>
using namespace std;

int n;
char s[5] = {'0', 'A', 'B', 'C'};
stack<int> a[5];

void move(int now, int next) {
    a[next].push(a[now].top());
    printf("%d from %c to %c\n", a[now].top(), s[now], s[next]);
    a[now].pop();
}

int main() {
    cin >> n;
    for (int i = n; i >= 1; i--)
        a[1].push(i);   // 初始时 A 柱从底到顶是 n, n-1, ..., 1

    if (n % 2 == 1) {   // 奇数时交换 B 和 C 的标签,保证最终移到 C
        s[2] = 'C';
        s[3] = 'B';
    }

    while (true) {
        // 第一步:移动圆盘 1
        int next;
        for (int i = 1; i <= 3; i++) {
            if (!a[i].empty() && a[i].top() == 1) {
                if (i == 3) next = 1;
                else next = i + 1;
                move(i, next);
                break;
            }
        }

        if (a[2].size() == n || a[3].size() == n) break; // 完成

        // 第二步:在剩余两柱间做合法移动
        int other1, other2;
        switch (next) {
            case 1: other1 = 2; other2 = 3; break;
            case 2: other1 = 3; other2 = 1; break;
            case 3: other1 = 1; other2 = 2; break;
        }
        if (a[other1].empty())
            move(other2, other1);
        else if (a[other2].empty())
            move(other1, other2);
        else {
            if (a[other1].top() < a[other2].top())
                move(other1, other2);
            else
                move(other2, other1);
        }
    }
    return 0;
}

代码的核心就是循环执行两步:移动最小盘,然后做剩下两柱之间的合法移动。当某一辅助柱(B 或 C)的圆盘数达到 n 时,任务完成。奇数个圆盘时交换了 B、C 的标签,最终会全部移到 C 上,符合题目要求。

最后

非递归的栈模拟方法不限于汉诺塔,很多递归问题(如二叉树的遍历、DFS、归并排序等)都可以通过手动维护栈来实现非递归版本。理解这种转换思路,对于把握递归的本质很有帮助。

目录

  1. 递归实现
  2. 非递归解法:用栈模拟或规律循环
  3. 最后
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 7款国内AI助手横评:豆包、元宝、千问、Kimi、DeepSeek、MiniMax、GLM
  • 2026 算法求职:为什么我劝你深耕多模态大模型
  • 2023年网络安全趋势观察:十个绕不开的方向
  • 用 MGeo 和 Neo4j 搭建中文地址语义知识图谱
  • 三维模型数据结构与存储方式解析
  • 大模型微调技术纵览:从全参微调到对齐方法的实践思考
  • 理解MySQL事务隔离:读未提交、读提交、可重复读和串行化
  • LeetCode 41 缺失的第一个正数:原地哈希两种思路
  • MATLAB 多模型 AI 编程助手:在编辑器内直接调用大模型
  • Go 仿真实践:免疫治疗门诊排队、irAE 与床位挤兑建模
  • 网络安全学习平台盘点:七个从新手到进阶的资源
  • Python 编程起步:变量、数据结构与面向对象实例
  • Python MCP Server 实战:从工具调用到业务数据查询
  • 微信小程序城市公交查询与失物招领系统的开发笔记
  • 链表细节与 Java LinkedList 实战
  • 配置 Spark SQL 访问 Hive 元数据
  • AgentScope Java 实战:构建 LLM 智能体与多智能体协作
  • 微信官方 Bot API 上手:ClawBot 插件的 iLink 协议细节
  • Trae + Figma:设计稿生成前端代码的配置流程与实现原理
  • 达梦数据库 Java 外部函数配置小记

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online