汉诺塔问题

汉诺塔问题

一、汉诺塔问题简介

汉诺塔(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)

七、结语

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

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

相关推荐
2301_790300965 分钟前
Python数据库操作:SQLAlchemy ORM指南
jvm·数据库·python
zfoo-framework14 分钟前
帧同步和状态同步
java
charlotte1024102416 分钟前
高并发:关于在等待学校教务系统选课时的碎碎念
java·运维·网络
m0_7369191021 分钟前
用Pandas处理时间序列数据(Time Series)
jvm·数据库·python
亓才孓21 分钟前
[JDBC]PreparedStatement替代Statement
java·数据库
_F_y43 分钟前
C++重点知识总结
java·jvm·c++
打工的小王1 小时前
Spring Boot(三)Spring Boot整合SpringMVC
java·spring boot·后端
毕设源码-赖学姐1 小时前
【开题答辩全过程】以 高校体育场馆管理系统为例,包含答辩的问题和答案
java·spring boot
lxysbly1 小时前
n64模拟器安卓版带金手指2026
android
我真会写代码1 小时前
SSM(指南一)---Maven项目管理从入门到精通|高质量实操指南
java·spring·tomcat·maven·ssm