【递归算法】汉诺塔

汉诺塔

汉诺塔问题是一个经典的递归算法问题,其核心思想是将一个由大到小叠放的圆盘从一根柱子移动到另一根柱子,过程中遵循以下规则:

  1. 每次只能移动一个圆盘;
  2. 圆盘只能在三根柱子之间移动;
  3. 小圆盘必须始终放在大圆盘之上。

定义数据结构

为了可视化显示移动过程,可以增加一个初始化方法,存储3根柱子对应有哪些盘子

python 复制代码
def init(num: int):
    column_dict = {}
    column_dict['A'] = list(range(num, 0, -1))
    column_dict['B'] = []
    column_dict['C'] = []
    print(0, column_dict)
    return column_dict

初始化方法定义了一个字典,该字典有 A、B、C 三根柱子,A柱存储了一个列表的数据,如果输入为3 那么A 柱存储的盘子编号是 [3,2,1], 调用init(3)方法

输出结果如下

bash 复制代码
>>> from tower_of_hanoi import init
>>> init(3)
0 {'A': [3, 2, 1], 'B': [], 'C': []}
{'A': [3, 2, 1], 'B': [], 'C': []}

列表 [3, 2, 1] 对应上图的盘子,数字越大,代表的盘子越大

移动1个盘子的方法

定义一个将一个盘子从源柱子 source 移动到 target 的方法,使用 step 列表记录当前移动了几次

python 复制代码
def move_one_step(column_dict, source: str, target: str, step: list):
    plate = column_dict[source].pop()
    column_dict[target].append(plate)
    step[0] = step[0] + 1
    print(step[0], column_dict)

def move_one_plate(column_dict, source: str, target: str, mid: str, step: list):
    move_one_step(column_dict, source, target, step)

运行 move_one_plate方法,查看终端的输出

python 复制代码
if __name__ == '__main__':
    step_list = [0]
    column_dict = init(1)
    move_one_plate(column_dict, 'A', 'C', 'B', step_list)
bash 复制代码
0 {'A': [1], 'B': [], 'C': []}
1 {'A': [], 'B': [], 'C': [1]}

移动2个盘子的方法

python 复制代码
def move_two_plate(column_dict, source: str, target: str, mid: str, step: list):
    move_one_step(column_dict, source, mid, step)
    move_one_step(column_dict, source, target, step)
    move_one_step(column_dict, mid, target, step)

运行移动2个盘子的方法,得到如下输出

python 复制代码
if __name__ == '__main__':
    step_list = [0]
    column_dict = init(1)
    move_one_plate(column_dict, 'A', 'C', 'B', step_list)
    print('-------------------------------------------')
    step_list = [0]
    column_dict = init(2)
    move_two_plate(column_dict, 'A', 'C', 'B', step_list)
bash 复制代码
0 {'A': [1], 'B': [], 'C': []}
1 {'A': [], 'B': [], 'C': [1]}
-------------------------------------------
0 {'A': [2, 1], 'B': [], 'C': []}
1 {'A': [2], 'B': [1], 'C': []}
2 {'A': [], 'B': [1], 'C': [2]}
3 {'A': [], 'B': [], 'C': [2, 1]}

移动3个盘子的方法

python 复制代码
def move_three_plate(column_dict, source: str, target: str, mid: str, step: list):
    move_one_step(column_dict, source, target, step)
    move_one_step(column_dict, source, mid, step)
    move_one_step(column_dict, target, mid, step)
    move_one_step(column_dict, source, target, step)
    move_one_step(column_dict, mid, source, step)
    move_one_step(column_dict, mid, target, step)
    move_one_step(column_dict, source, target, step)

运行移动3个盘子的方法,得到如下输出

python 复制代码
if __name__ == '__main__':
    step_list = [0]
    column_dict = init(1)
    move_one_plate(column_dict, 'A', 'C', 'B', step_list)
    print('-------------------------------------------')
    step_list = [0]
    column_dict = init(2)
    move_two_plate(column_dict, 'A', 'C', 'B', step_list)
    print('-------------------------------------------')
    column_dict = init(3)
    step_list = [0]
    move_three_plate(column_dict, 'A', 'C', 'B', step_list)
bash 复制代码
0 {'A': [1], 'B': [], 'C': []}
1 {'A': [], 'B': [], 'C': [1]}
-------------------------------------------
0 {'A': [2, 1], 'B': [], 'C': []}
1 {'A': [2], 'B': [1], 'C': []}
2 {'A': [], 'B': [1], 'C': [2]}
3 {'A': [], 'B': [], 'C': [2, 1]}
-------------------------------------------
0 {'A': [3, 2, 1], 'B': [], 'C': []}
1 {'A': [3, 2], 'B': [], 'C': [1]}
2 {'A': [3], 'B': [2], 'C': [1]}
3 {'A': [3], 'B': [2, 1], 'C': []}
4 {'A': [], 'B': [2, 1], 'C': [3]}
5 {'A': [1], 'B': [2], 'C': [3]}
7 {'A': [], 'B': [], 'C': [3, 2, 1]}

从第2条分割线开始看,初始化A柱有3个盘子,我们想要把他们都移动到C柱去,那么首先需要把[2,1] 盘子从A柱移动到B柱,从第一条分割线之后看,把2个盘子[2,1]从A柱移动到C柱的方法已经有了,我们可以直接调用之前移动2个盘子的方法,该变一下参数位置即可,新增以下方法 move_three_plate2

python 复制代码
def move_three_plate2(column_dict, source: str, target: str, mid: str, step: list):
    move_two_plate(column_dict, source, mid, target, step)

在主函数中运行一下

python 复制代码
if __name__ == '__main__':
    step_list = [0]
    column_dict = init(1)
    move_one_plate(column_dict, 'A', 'C', 'B', step_list)
    print('-------------------------------------------')
    step_list = [0]
    column_dict = init(2)
    move_two_plate(column_dict, 'A', 'C', 'B', step_list)
    print('-------------------------------------------')
    column_dict = init(3)
    step_list = [0]
    move_three_plate(column_dict, 'A', 'C', 'B', step_list)
    print('-------------------------------------------')
    column_dict = init(3)
    step_list = [0]
    move_three_plate2(column_dict, 'A', 'C', 'B', step_list)
bash 复制代码
0 {'A': [1], 'B': [], 'C': []}
1 {'A': [], 'B': [], 'C': [1]}
-------------------------------------------
0 {'A': [2, 1], 'B': [], 'C': []}
1 {'A': [2], 'B': [1], 'C': []}
2 {'A': [], 'B': [1], 'C': [2]}
3 {'A': [], 'B': [], 'C': [2, 1]}
-------------------------------------------
0 {'A': [3, 2, 1], 'B': [], 'C': []}
1 {'A': [3, 2], 'B': [], 'C': [1]}
2 {'A': [3], 'B': [2], 'C': [1]}
3 {'A': [3], 'B': [2, 1], 'C': []}
4 {'A': [], 'B': [2, 1], 'C': [3]}
5 {'A': [1], 'B': [2], 'C': [3]}
6 {'A': [1], 'B': [], 'C': [3, 2]}
7 {'A': [], 'B': [], 'C': [3, 2, 1]}
-------------------------------------------
0 {'A': [3, 2, 1], 'B': [], 'C': []}
1 {'A': [3, 2], 'B': [], 'C': [1]}
2 {'A': [3], 'B': [2], 'C': [1]}
3 {'A': [3], 'B': [2, 1], 'C': []}

调用 move_two_plate(column_dict, 'A', 'B', 'C', step) 完成了移动3个盘子的前3步,此时可以将A柱的最大的盘子移动到C柱啦,而且还复用了之前的代码。修改move_three_plate2 方法,移动3号盘从A到C

python 复制代码
def move_three_plate2(column_dict, source: str, target: str, mid: str, step: list):
    move_two_plate(column_dict, source, mid, target, step)
    move_one_step(column_dict, source, target, step)

运行代码,得到以下输出

bash 复制代码
0 {'A': [1], 'B': [], 'C': []}
1 {'A': [], 'B': [], 'C': [1]}
-------------------------------------------
0 {'A': [2, 1], 'B': [], 'C': []}
1 {'A': [2], 'B': [1], 'C': []}
2 {'A': [], 'B': [1], 'C': [2]}
3 {'A': [], 'B': [], 'C': [2, 1]}
-------------------------------------------
0 {'A': [3, 2, 1], 'B': [], 'C': []}
1 {'A': [3, 2], 'B': [], 'C': [1]}
2 {'A': [3], 'B': [2], 'C': [1]}
3 {'A': [3], 'B': [2, 1], 'C': []}
4 {'A': [], 'B': [2, 1], 'C': [3]}
5 {'A': [1], 'B': [2], 'C': [3]}
6 {'A': [1], 'B': [], 'C': [3, 2]}
7 {'A': [], 'B': [], 'C': [3, 2, 1]}
-------------------------------------------
0 {'A': [3, 2, 1], 'B': [], 'C': []}
1 {'A': [3, 2], 'B': [], 'C': [1]}
2 {'A': [3], 'B': [2], 'C': [1]}
3 {'A': [3], 'B': [2, 1], 'C': []}
4 {'A': [], 'B': [2, 1], 'C': [3]}

观察当前的数据,可以很容易发现,我们还需要将B柱的2个盘子[2,1]移动到C柱,修改 move_three_plate2,再次调用 move_two_plate 方法,将B柱的2个盘子[2,1]移动到C柱

python 复制代码
def move_three_plate2(column_dict, source: str, target: str, mid: str, step: list):
    move_two_plate(column_dict, source, mid, target, step)
    move_one_step(column_dict, source, target, step)
    move_two_plate(column_dict, mid, target, source, step)

运行代码,得到以下输出

bash 复制代码
0 {'A': [1], 'B': [], 'C': []}
1 {'A': [], 'B': [], 'C': [1]}
-------------------------------------------
0 {'A': [2, 1], 'B': [], 'C': []}
1 {'A': [2], 'B': [1], 'C': []}
2 {'A': [], 'B': [1], 'C': [2]}
3 {'A': [], 'B': [], 'C': [2, 1]}
-------------------------------------------
0 {'A': [3, 2, 1], 'B': [], 'C': []}
1 {'A': [3, 2], 'B': [], 'C': [1]}
2 {'A': [3], 'B': [2], 'C': [1]}
3 {'A': [3], 'B': [2, 1], 'C': []}
4 {'A': [], 'B': [2, 1], 'C': [3]}
5 {'A': [1], 'B': [2], 'C': [3]}
6 {'A': [1], 'B': [], 'C': [3, 2]}
7 {'A': [], 'B': [], 'C': [3, 2, 1]}
-------------------------------------------
0 {'A': [3, 2, 1], 'B': [], 'C': []}
1 {'A': [3, 2], 'B': [], 'C': [1]}
2 {'A': [3], 'B': [2], 'C': [1]}
3 {'A': [3], 'B': [2, 1], 'C': []}
4 {'A': [], 'B': [2, 1], 'C': [3]}
5 {'A': [1], 'B': [2], 'C': [3]}
6 {'A': [1], 'B': [], 'C': [3, 2]}
7 {'A': [], 'B': [], 'C': [3, 2, 1]}

从移动数据可以看出 move_three_plate2 实现了移动3个盘子的最优方案,和之前的方法 move_three_plate 移动盘子过程一致。对比 move_three_plate2move_two_plate 我们发现参数都是相同的,只不过所移动盘子的数量不一样,可以尝试修改成递归的方法,复制 move_three_plate2 方法命名为move,

将方法里面的 move_three_plate 也修改成 move,得到如下新方法

python 复制代码
def move(column_dict, source: str, target: str, mid: str, step: list):
    move(column_dict, source, mid, target, step)
    move_one_step(column_dict, source, target, step)
    move(column_dict, mid, target, source, step)

在主函数中运行 move 方法

复制代码
if __name__ == '__main__':
    step_list = [0]
    column_dict = init(1)
    move_one_plate(column_dict, 'A', 'C', 'B', step_list)
    print('-------------------------------------------')
    step_list = [0]
    column_dict = init(2)
    move_two_plate(column_dict, 'A', 'C', 'B', step_list)
    print('-------------------------------------------')
    column_dict = init(3)
    step_list = [0]
    move_three_plate(column_dict, 'A', 'C', 'B', step_list)
    print('-------------------------------------------')
    column_dict = init(3)
    step_list = [0]
    move_three_plate2(column_dict, 'A', 'C', 'B', step_list)
    print('-------------------------------------------')
    column_dict = init(3)
    step_list = [0]
    move(column_dict, 'A', 'C', 'B', step_list)

终端输出如下

bash 复制代码
Exception has occurred: RecursionError
maximum recursion depth exceeded

方法一直在递归调用自己,没有递归的出口,达到方法递归调用栈的最大深度报错。因此,可以增加一个参数n,来记录当前需要移动盘子的个数,当n=1时候,只需要移动1最后一个盘子,完成之后,退出递归。修改之后的 move 方法如下:

python 复制代码
def move(column_dict, n: int, source: str, target: str, mid: str, step: list):
    if n == 1:
        move_one_step(column_dict, source, target, step)
        return
    move(column_dict, n - 1, source, mid, target, step)
    move_one_step(column_dict, source, target, step)
    move(column_dict, n - 1, mid, target, source, step)

运行主函数,得到如下输出

python 复制代码
if __name__ == '__main__':
    step_list = [0]
    column_dict = init(1)
    move_one_plate(column_dict, 'A', 'C', 'B', step_list)
    print('-------------------------------------------')
    step_list = [0]
    column_dict = init(2)
    move_two_plate(column_dict, 'A', 'C', 'B', step_list)
    print('-------------------------------------------')
    column_dict = init(3)
    step_list = [0]
    move_three_plate(column_dict, 'A', 'C', 'B', step_list)
    print('-------------------------------------------')
    column_dict = init(3)
    step_list = [0]
    move_three_plate2(column_dict, 'A', 'C', 'B', step_list)
    print('-------------------------------------------')
    column_dict = init(3)
    step_list = [0]
    move(column_dict, 3, 'A', 'C', 'B', step_list)
bash 复制代码
0 {'A': [1], 'B': [], 'C': []}
1 {'A': [], 'B': [], 'C': [1]}
-------------------------------------------
0 {'A': [2, 1], 'B': [], 'C': []}
1 {'A': [2], 'B': [1], 'C': []}
2 {'A': [], 'B': [1], 'C': [2]}
3 {'A': [], 'B': [], 'C': [2, 1]}
-------------------------------------------
0 {'A': [3, 2, 1], 'B': [], 'C': []}
1 {'A': [3, 2], 'B': [], 'C': [1]}
2 {'A': [3], 'B': [2], 'C': [1]}
3 {'A': [3], 'B': [2, 1], 'C': []}
4 {'A': [], 'B': [2, 1], 'C': [3]}
5 {'A': [1], 'B': [2], 'C': [3]}
6 {'A': [1], 'B': [], 'C': [3, 2]}
7 {'A': [], 'B': [], 'C': [3, 2, 1]}
-------------------------------------------
0 {'A': [3, 2, 1], 'B': [], 'C': []}
1 {'A': [3, 2], 'B': [], 'C': [1]}
2 {'A': [3], 'B': [2], 'C': [1]}
3 {'A': [3], 'B': [2, 1], 'C': []}
4 {'A': [], 'B': [2, 1], 'C': [3]}
5 {'A': [1], 'B': [2], 'C': [3]}
6 {'A': [1], 'B': [], 'C': [3, 2]}
7 {'A': [], 'B': [], 'C': [3, 2, 1]}
-------------------------------------------
0 {'A': [3, 2, 1], 'B': [], 'C': []}
1 {'A': [3, 2], 'B': [], 'C': [1]}
2 {'A': [3], 'B': [2], 'C': [1]}
3 {'A': [3], 'B': [2, 1], 'C': []}
4 {'A': [], 'B': [2, 1], 'C': [3]}
5 {'A': [1], 'B': [2], 'C': [3]}
6 {'A': [1], 'B': [], 'C': [3, 2]}
7 {'A': [], 'B': [], 'C': [3, 2, 1]}

最后一条分割线之后的数据就是调用 move 方法移动的输出,完成了3个盘子的移动过程。至此,😁递归的推导过程也完成了

完整的代码

python 复制代码
def init(num: int):
    column_dict = {}
    column_dict['A'] = list(range(num, 0, -1))
    column_dict['B'] = []
    column_dict['C'] = []
    print(0, column_dict)
    return column_dict

def move_one_step(column_dict, source: str, target: str, step: list):
    plate = column_dict[source].pop()
    column_dict[target].append(plate)
    step[0] = step[0] + 1
    print(step[0], column_dict)


def move_one_plate(column_dict, source: str, target: str, mid: str, step: list):
    move_one_step(column_dict, source, target, step)

def move_two_plate(column_dict, source: str, target: str, mid: str, step: list):
    move_one_step(column_dict, source, mid, step)
    move_one_step(column_dict, source, target, step)
    move_one_step(column_dict, mid, target, step)

def move_three_plate(column_dict, source: str, target: str, mid: str, step: list):
    move_one_step(column_dict, source, target, step)
    move_one_step(column_dict, source, mid, step)
    move_one_step(column_dict, target, mid, step)
    move_one_step(column_dict, source, target, step)
    move_one_step(column_dict, mid, source, step)
    move_one_step(column_dict, mid, target, step)
    move_one_step(column_dict, source, target, step)

def move_three_plate2(column_dict, source: str, target: str, mid: str, step: list):
    move_two_plate(column_dict, source, mid, target, step)
    move_one_step(column_dict, source, target, step)
    move_two_plate(column_dict, mid, target, source, step)

def move(column_dict, n: int, source: str, target: str, mid: str, step: list):
    if n == 1:
        move_one_step(column_dict, source, target, step)
        return
    move(column_dict, n - 1, source, mid, target, step)
    move_one_step(column_dict, source, target, step)
    move(column_dict, n - 1, mid, target, source, step)



if __name__ == '__main__':
    step_list = [0]
    column_dict = init(1)
    move_one_plate(column_dict, 'A', 'C', 'B', step_list)
    print('-------------------------------------------')
    step_list = [0]
    column_dict = init(2)
    move_two_plate(column_dict, 'A', 'C', 'B', step_list)
    print('-------------------------------------------')
    column_dict = init(3)
    step_list = [0]
    move_three_plate(column_dict, 'A', 'C', 'B', step_list)
    print('-------------------------------------------')
    column_dict = init(3)
    step_list = [0]
    move_three_plate2(column_dict, 'A', 'C', 'B', step_list)
    print('-------------------------------------------')
    column_dict = init(3)
    step_list = [0]
    move(column_dict, 3, 'A', 'C', 'B', step_list)
    print('-------------------------------------------')
    for n in range(4, 10):
        column_dict = init(n)
        step_list = [0]
        move(column_dict, n, 'A', 'C', 'B', step_list)
        print('-------------------------------------------')

输出太长了,这里就不展示了

相关推荐
Mr YiRan3 小时前
C++面向对象继承与操作符重载
开发语言·c++·算法
Emotional。3 小时前
2025 年度技术总结与规划:AI 时代的开发者成长之路
人工智能·python·ai·langchain
witAI6 小时前
**AI仿真人剧制作软件2025推荐,解锁沉浸式数字内容创作
人工智能·python
蚊子码农7 小时前
算法题解记录--239滑动窗口最大值
数据结构·算法
liliangcsdn7 小时前
A3C算法从目标函数到梯度策略的探索
算法
Codefengfeng7 小时前
Python Base环境中加包的方法
开发语言·python
清水白石0087 小时前
《Python 编程全景解析:从核心精要到测试替身(Test Doubles)五大武器的实战淬炼》
开发语言·python
陈天伟教授8 小时前
人工智能应用- 材料微观:06.GAN 三维重构
人工智能·神经网络·算法·机器学习·重构·推荐算法
如若1238 小时前
AutoDL云服务器 NVIDIA 570驱动 EGL渲染修复全记录
运维·服务器·python
liliangcsdn8 小时前
A3C强化学习算法的探索和学习
算法