汉诺塔问题

汉诺塔问题

一、汉诺塔问题简介

汉诺塔(Tower of Hanoi)是一个经典的递归问题,最早由法国数学家爱德华·卢卡斯(Édouard Lucas)在 1883 年提出。问题描述如下:

  • 有三根杆子,分别标记为 A、B、C。
  • 起始时,所有的盘子都在杆子 A 上,并且按大小顺序从下到上排列(即最小的盘子在最上面,最大的盘子在最下面)。
  • 目标是将所有盘子从杆子 A 移动到杆子 C。
  • 每次只能移动一个盘子,且每次移动时,盘子只能放在比它大的盘子上,或者杆子为空时可以直接放置。

二、递归解法

汉诺塔问题的关键在于递归的思想。为了将 n 个盘子从柱子 A 移动到柱子 C,我们可以分为两个部分:

  1. 将上面 n-1 个盘子从 A 移动到 B(使用 C 作为辅助柱子)。
  2. 将第 n 个盘子(即最大的盘子)从 A 移动到 C
  3. 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

  • 递归步骤

    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') 时,输出如下:

复制代码
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)

七、结语

通过递归的方式解决汉诺塔问题,能够帮助我们理解递归思想和如何将一个复杂问题分解成更小的子问题。汉诺塔问题在许多算法和数据结构的教材中都有详细的讲解,是学习递归的经典例子。

递归的最大优势在于代码简洁,但也要注意,当问题规模较大时,递归的效率会有所下降,特别是在执行时间较长的情况下。如果你想在更大规模的输入下优化性能,可以考虑使用动态规划或非递归的方式来解决。

相关推荐
小猿姐3 小时前
实测对比:哪款开源 Kubernetes MySQL Operator 最值得用?(2026 深度评测)
数据库·mysql·云原生
一灯架构4 小时前
90%的人答错!一文带你彻底搞懂ArrayList
java·后端
倔强的石头_5 小时前
从 “存得下” 到 “算得快”:工业物联网需要新一代时序数据平台
数据库
Y4090015 小时前
【多线程】线程安全(1)
java·开发语言·jvm
TDengine (老段)6 小时前
TDengine IDMP 可视化 —— 分享
大数据·数据库·人工智能·时序数据库·tdengine·涛思数据·时序数据
布局呆星6 小时前
SpringBoot 基础入门
java·spring boot·spring
风吹迎面入袖凉6 小时前
【Redis】Redisson的可重入锁原理
java·redis
GottdesKrieges6 小时前
OceanBase数据库备份配置
数据库·oceanbase
w6100104666 小时前
cka-2026-ConfigMap
java·linux·cka·configmap