汉诺塔问题
一、汉诺塔问题简介
汉诺塔(Tower of Hanoi)是一个经典的递归问题,最早由法国数学家爱德华·卢卡斯(Édouard Lucas)在 1883 年提出。问题描述如下:
- 有三根杆子,分别标记为 A、B、C。
- 起始时,所有的盘子都在杆子 A 上,并且按大小顺序从下到上排列(即最小的盘子在最上面,最大的盘子在最下面)。
- 目标是将所有盘子从杆子 A 移动到杆子 C。
- 每次只能移动一个盘子,且每次移动时,盘子只能放在比它大的盘子上,或者杆子为空时可以直接放置。
二、递归解法

汉诺塔问题的关键在于递归的思想。为了将 n 个盘子从柱子 A 移动到柱子 C,我们可以分为两个部分:
- 将上面
n-1个盘子从 A 移动到 B(使用 C 作为辅助柱子)。 - 将第
n个盘子(即最大的盘子)从 A 移动到 C。 - 将
n-1个盘子从 B 移动到 C(使用 A 作为辅助柱子)。
我们可以通过递归将问题不断分解,直到 n = 1 时,移动盘子的操作非常简单。
三、递归实现
下面是一个 Python和C++实现的递归解法,展示了如何一步步将盘子从起始柱子移动到目标柱子。
c
#include<iostream>
using namespace std;
void move(int num, int pan, char pos1, char pos2){
cout << num << ". Move disk " << pan << " from " << pos1 << " to " << pos2 << endl;
}
void hanoi(int& idx, int n, char X, char Y, char Z){ //y是辅助
if(n == 1){
move(idx++, 1, X, Z);
}else{
hanoi(idx, n-1, X, Z, Y); // 先把n-1个放在y上
move(idx++, n, X, Z); // 最后一个大的盘子放在z上
hanoi(idx, n-1, Y, X, Z); // 这下n-1个盘在y上,y变为原先的x,x为空,成为新的辅助,z仍是目标
}
}
int main(){
int nums;
int idx = 1;
while(cin >> nums){
hanoi(idx, nums, 'X', 'Y', 'Z');
cout << endl;
idx = 1;
}
return 0;
}
python
def hanoi(n, source, target, auxiliary):
"""
解决汉诺塔问题的递归函数
:param n: 盘子的数量
:param source: 源柱子
:param target: 目标柱子
:param auxiliary: 辅助柱子
"""
# 递归基准情况:只有一个盘子时,直接从源柱子移动到目标柱子
if n == 1:
print(f"Move disk 1 from {source} to {target}")
return
# 步骤1: 将 n-1 个盘子从源柱子移动到辅助柱子
hanoi(n-1, source, auxiliary, target)
# 步骤2: 将第 n 个盘子从源柱子移动到目标柱子
print(f"Move disk {n} from {source} to {target}")
# 步骤3: 将 n-1 个盘子从辅助柱子移动到目标柱子
hanoi(n-1, auxiliary, target, source)
# 例子:解决 3 个盘子的汉诺塔问题
hanoi(3, 'A', 'C', 'B')
四、解释代码
-
hanoi(n, source, target, auxiliary):该函数解决将n个盘子从source移动到target,并使用auxiliary作为辅助柱子。 -
递归基准情况 :如果只有一个盘子(
n == 1),则直接将盘子从source移动到target。 -
递归步骤:
- 将
n-1个盘子从源柱子移动到辅助柱子 。这一步是递归调用:hanoi(n-1, source, auxiliary, target)。 - 将第
n个盘子从源柱子移动到目标柱子 ,这是一个直接的操作,打印出移动的步骤:print(f"Move disk {n} from {source} to {target}")。 - 将
n-1个盘子从辅助柱子移动到目标柱子 。这也是递归调用:hanoi(n-1, auxiliary, target, source)。
- 将
五、示例输出
假设我们解决的是 3 个盘子的汉诺塔问题,执行 hanoi(3, 'A', 'C', 'B') 时,输出如下:
Move disk 1 from A to C
Move disk 2 from A to B
Move disk 1 from C to B
Move disk 3 from A to C
Move disk 1 from B to A
Move disk 2 from B to C
Move disk 1 from A to C
六、时间复杂度和空间复杂度
时间复杂度:
-
汉诺塔问题的递归树呈二叉树形状,每次递归调用都会拆分成两个子问题,因此其时间复杂度是指数级的,即 O(2^n)。
具体来说,对于
n个盘子,递归树的高度为n,每一层都会有多个子问题。计算需要的移动次数也会是2^n - 1次。
空间复杂度:
- 空间复杂度主要受递归栈的影响。在最坏的情况下,递归深度为
n,因此空间复杂度为 O(n)。
七、结语
通过递归的方式解决汉诺塔问题,能够帮助我们理解递归思想和如何将一个复杂问题分解成更小的子问题。汉诺塔问题在许多算法和数据结构的教材中都有详细的讲解,是学习递归的经典例子。
递归的最大优势在于代码简洁,但也要注意,当问题规模较大时,递归的效率会有所下降,特别是在执行时间较长的情况下。如果你想在更大规模的输入下优化性能,可以考虑使用动态规划或非递归的方式来解决。