汉诺塔问题

汉诺塔问题

一、汉诺塔问题简介

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

七、结语

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

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

相关推荐
星马梦缘6 分钟前
数据库作战记录 实验7、8
数据库·sql·oracle
alexhilton16 分钟前
揭密:Compose应用如何做到启动提升34%
android·kotlin·android jetpack
超梦dasgg16 分钟前
智慧充电系统设备管理服务对外接口实现方案
java·spring·微服务
安逸sgr30 分钟前
Hermes Agent + Obsidian 打造第二大脑(六):分层记忆系统的设计逻辑——L0/L1/L2/L3 四层记忆详解
数据库·agent·知识库·hermes·hermesagent
xiaoye37081 小时前
Spring 事务传播机制 + 隔离级别
java·后端·spring
苍煜1 小时前
一篇讲懂分库分表:概念、spirngboot实战
数据库·oracle
Arya_aa1 小时前
数据字典模块–JSR303参数校验
java
梦想画家1 小时前
PostgreSQL 物化视图实战:从数据固化到智能刷新的全链路指南
数据库·postgresql·物化视图
weoptions1 小时前
简单sql注入中如何通过简单语句判断注入类型&注入方法
数据库·sql
小短腿的代码世界1 小时前
Qt数据库编程深度解析:从SQL基础到ORM架构设计
数据库·sql·qt