汉诺塔是经典的递归算法教学工具,也是一个优雅的数学谜题。将所有圆盘从起点柱移到终点柱,每次只能移动一个盘,且大盘不能放在小盘上方。这个在线版本支持 3-6 层难度,每一步都配有递归算法解读,帮助你真正理解递归思维。
3 盘入门(7步)→ 6 盘挑战(63步),循序渐进掌握递归思维。
每移动一步,下方都会出现这一步在递归调用树中的位置和意义。
一键观看 AI 演示最优解法,支持慢/中/快三档速度,可暂停回看。
实时计算你的步数与最优解的比率,挑战 100% 完美通关。
递归是一种解决问题的方法:把一个大问题拆成结构相同但规模更小的子问题,直到小到可以直接解决。想象俄罗斯套娃——打开一个,里面还有一个,直到最小的那个。汉诺塔是理解递归最直观的例子,因为它的问题结构天然就是递归的。
递归有两个要素:基础情形(最小的套娃,直接处理)和递归步骤(打开当前套娃,处理里面更小的)。如果缺少基础情形,递归会无限循环下去——就像永远打不完的套娃。
要把 n 个盘从 A 柱移到 C 柱,你面对的核心矛盾是:最大的盘被压在最下面,上面所有盘不搬走它就动不了。于是解法自然浮现——
hanoi(n, 起点, 终点, 中转):
if n == 1: move(起点 → 终点) // 基础情形:只有1个盘,直接移
else:
hanoi(n-1, 起点, 中转, 终点) // ① 把上面 n-1 个盘搬到中转柱
move(起点 → 终点) // ② 最大盘直接移到终点
hanoi(n-1, 中转, 终点, 起点) // ③ n-1 个盘从中转搬到终点
关键洞察:步骤 ① 和 ③ 跟原问题结构完全一样,只是规模少了一个盘、柱子角色互换了。这就是递归——你不需要知道 n-1 个盘怎么搬,你只需要相信它能搬好(递归信仰之跃),然后处理当前层的那一个盘就行。
以 3 个盘(A→C)为例,递归展开后的 7 步移动:
hanoi(3, A→C, 中转B)
├─ hanoi(2, A→B, 中转C)
│ ├─ hanoi(1, A→C) → 步骤1: A→C 移动盘1
│ ├─ 步骤2: A→B 移动盘2
│ └─ hanoi(1, C→B) → 步骤3: C→B 移动盘1
├─ 步骤4: A→C 移动盘3(最大盘!)
└─ hanoi(2, B→C, 中转A)
├─ hanoi(1, B→A) → 步骤5: B→A 移动盘1
├─ 步骤6: B→C 移动盘2
└─ hanoi(1, A→C) → 步骤7: A→C 移动盘1
注意对称性:步骤 1-3 把上面 2 个盘搬到 B 柱,步骤 4 搬最大盘,步骤 5-7 再把 2 个盘从 B 搬到 C。前半段和后半段结构完全相同,只是柱子角色对调了。
计算机执行递归时,每次调用函数都会在内存中创建一个「栈帧」,记录当前执行到哪里、参数是什么。当递归深入时栈帧不断堆叠,触底(基础情形)后再一层层返回。
调用栈变化(3 盘,执行到步骤 1 时):
┌─────────────────────────────┐
│ hanoi(1, A→C) │ ← 栈顶:当前执行
├─────────────────────────────┤
│ hanoi(2, A→B, 中转C) 步骤① │ ← 等待子调用返回
├─────────────────────────────┤
│ hanoi(3, A→C, 中转B) 步骤① │ ← 等待子调用返回
└─────────────────────────────┘
栈的最大深度等于盘的数量 n。这就是递归的空间代价——n 个盘需要 n 层栈帧。对于汉诺塔来说 n 很小(通常不超过 20),完全不是问题。但在其他场景中,递归过深会导致「栈溢出」(Stack Overflow)。
汉诺塔也可以用迭代(循环)来解,不需要递归。迭代解法基于一个巧妙的规律:
最小盘每隔一步移动一次,方向固定(n 为奇数时逆时针 A→C→B→A,n 为偶数时顺时针 A→B→C→A)
非最小盘的移动步只有一种合法选择(因为另一个柱子上要么没盘,要么顶部盘更小)
| 递归 | 迭代 | |
|---|---|---|
| 代码量 | 5 行,极简 | 15-20 行 |
| 可读性 | 直观,与问题结构一致 | 需要理解移动规律 |
| 空间 | O(n) 栈空间 | O(1) 常数空间 |
| 时间 | O(2ⁿ) | O(2ⁿ),相同 |
两种方法步数完全一样,区别在于思维方式。递归是「自顶向下」的分解思维,迭代是「自底向上」的规律发现。对于汉诺塔,递归解法更自然、更容易理解和证明正确性。
汉诺塔的起源是一个传说:在印度贝拿勒斯的神庙中,僧侣们正在移动一组 64 个金盘。当所有盘移动完成时,世界将会终结。
这个传说恰好揭示了指数增长的惊人威力。64 个盘需要多少步?
2⁶⁴ - 1 = 18,446,744,073,709,551,615 步
假设每秒移动一个盘,需要约 5845 亿年——是宇宙年龄(138 亿年)的 42 倍。
这就是为什么理解 2ⁿ 增长如此重要。从 6 盘的 63 步到 64 盘的 1800 亿亿步,每多一个盘步数翻倍。递归让你用 5 行代码描述这个解法,但宇宙的寿命都不够执行完。算法的时间复杂度,才是真正的瓶颈。
从递归结构直接可以推导出步数公式。设 T(n) 为 n 个盘的最少步数:
T(1) = 1
T(n) = T(n-1) + 1 + T(n-1) = 2·T(n-1) + 1
展开这个递推式:T(n) = 2·T(n-1) + 1 = 2·(2·T(n-2) + 1) + 1 = ... = 2ⁿ - 1。
汉诺塔的最少步数为 2^n - 1,其中 n 是圆盘数量。3个盘最少7步,4个盘15步,5个盘31步,6个盘63步。这是数学证明的绝对下界,不可能更少。
这个约束正是汉诺塔全部复杂性的来源。去掉这条规则,问题立刻变得微不足道(直接搬就行)。正是因为这个限制,才产生了递归结构——你必须先把上面的盘搬走,才能动下面的盘。
需要每一步都走最优解的路径。关键策略:最小盘始终往同一个方向轮转(3盘时逆时针,4盘时顺时针),非最小盘永远只有一种合法移动。掌握这个规律就能达到完美解。
使用经典的递归算法:hanoi(n, from, to, via)。当 n=1 时直接移动(基础情形),否则先递归移动上面 n-1 个盘到中转柱,再移动最大盘到目标柱,最后递归移动 n-1 个盘到目标柱。