Hanoi汉诺塔代码原理

前言

最近在学Python,遇到了经典的递归问题,汉诺塔。算法原理很简单,代码实现也很简单,可谓大道至简。但是这代码的理解,却稍微让人抓狂,特别是递归调用的参数位置。故本文,重点阐述代码实现,而不注重原理阐述。

汉诺塔算法原理

汉诺塔图示

 

1,当 n = 1,A直接移动到C

2,当 n > 1 ,设此时 n=n :

  • 把 A 柱子上面的 (n-1) 个盘子,从 A 移动到 B;

  • 把 A 柱子上面的第 n 个盘子由 A 移动到 C;

  • 把第一步 B 柱子上的 (n-1) 个盘子由 B 移动到 C

PS:

     用句大白话来解释这个算法,就是不管有几个盘子(当然至少得2个,一个就太简单了),就把它看作是2个盘子,第一步,把上面的一个盘子,放到辅助列上去,再把下面的盘子放到目的列,然后再把辅助列的盘子放到目的列。完美,大功告成。以图示的ABC列为例,

汉诺塔图示

Python 代码实现

steps = 0    # 步骤序号


def hanoi(afford, mid, accept, n):  # afford 最初圆盘所在列,提供圆盘的  ||  mid 中间辅助列   ||  accept 接受列,圆盘最终移动的列  ||  n 最初的圆盘的数量
    global steps
    if n == 1:
        steps = steps + 1
        print("[STEP{:>4}] {}->{}".format(steps, afford, accept))
    else:
        hanoi(afford, accept, mid, n - 1)
        steps = steps + 1
        print("[STEP{:>4}] {}->{}".format(steps, afford, accept))
        hanoi(mid, afford, accept, n - 1)


hanoi('A', 'B', 'C', 2)

运行结果

[STEP   1] A->B
[STEP   2] A->C
[STEP   3] B->C
[Finished in 0.1s]

代码的前世今生

1,还是以上面的图,ABC列为例。我们的终极目标是把A列上的圆盘,全部转移到C列上去。我们想要这样的一个hanoi 函数,它可以帮我们把 A 列的圆盘放到C列上。那么这个函数需要几个参数呢?看图就知道,我们肯定至少需要3个参数,一个来当A列,一个来当C列,一个来当辅助的B列。那圆盘的个数怎么表示?所以我们再加一个圆盘的个数,这就是第四个参数。嗯,目前能想到的,也就这么多了,那就先试试吧。

2,按照刚刚的想法,我们弄出这样一个hanoi,hanoi(参数1,参数2,参数3,参数4) ,hanoi 从出生时起,就有这样的功能,把第一列的圆盘,放到第三列上面去,这里先把参数一放到A列,把参数三放到C列。参数二,是一个辅助参数,,放到B列。参数四,表示一共有多少个圆盘,这里就不说放到D列了,因为看图就知道,我们只需要3列就行,参数4就相当于一个数量标志,附加在ABC列上的一个数量。

3,为了更直观,我们把参数一二三四,弄个具体的名字。于是,天空一声惊雷,Hanoi闪亮登场 。 hanoi(afford, mid, accept, n) 。把 hanoi 的前3个参数,afford,mid,accept,看成ABC三列。我们知道(因为我们就是按照我们最初的想法设计的hanoi),hanoi 提供这样一个功能,把A列的圆盘,放到C列上去。 这里用的是列,不是参数的名字,因为参数的名字你可以随便取,但是ABC列的位置已经固定了。当你定义出 hanoi 的时候,hanoi 的功能和ABC列的位置就确定下来了,就是把A列的圆盘放到C列上去。(当然你可以定义,A列放到B列,或者B列放到C列)每调用一次hanoi,他就会把A列的圆盘,放到C列上去。这里稍微有点绕,我用故事来说。afford,mid,accept 是三个特务,afford 要把自己的情报(圆盘)通过 mid 这个线人,传递给老板 accept,由于敌人内部管理严格,只有通过特定的装置(Hanoi)才能传递信息,这个特定的装置Hanoi,是这样工作的,它有三个座位ABC,每次必须同时坐三个人,坐上后,能把A座位上的人的信息通过B座位上的人,传递给C座位上的人。至于afford,mid,accept 这三个人谁坐A,谁坐B,谁坐C,不一定,看谁想传递信息给谁。反正坐在A座位上的人,会把信息传递给C座位上的人。

4,有了大致的思路,我们就来看看实现。

n =1,即 afford 上面只有一个圆盘

一层汉诺塔

目标:

      此时afford在A列,accept在C列,直接把afford上的一个圆盘,放到第三列的accept,不需要通过B来辅助。

实现:

def hanoi(afford, mid, accept, n):  # afford 最初圆盘所在列,提供圆盘的    mid 中间辅助列   accept 接受列,圆盘最终移动的列  n 最初的圆盘的数量
    global steps
    if n == 1:
        steps = steps + 1
        print("[STEP{:>4}] {}->{}".format(steps, afford, accept))

 

n = n,即afford上面有 n 个圆盘

n层汉诺塔(把上面的一个看作n-1个)

 目标:

         afford 有n个圆盘,要把上面n-1个圆盘,先放到辅助列mid上去

实现:

       我们知道调用hanoi,就会把第一列的圆盘,放到第三列,那么我们现在在调用hanoi的时候,afford需要坐在A列,mid坐到C列,hanoi就可以把afford上面n-1个圆盘转移到mid,此时注意,我们只转移了n-1个,所以hanoi你们的n这个参数,是n-1

else:
        hanoi(afford, accept, mid, n - 1)

调用完成后,此时就是这样的,

图示
n-1个圆盘被移到了mid列上

接下来,我们要将afford上的这一个最大的圆盘,交换到accept上,执行 print 即可。(因为我们的print 永远就是将afford上的圆盘,放到accept上)

print("[STEP{:>4}] {}->{}".format(steps, afford, accept))

执行完成后,是这样的

afford 上的一个圆盘,放到了C上

 

最后,我们只需要将辅助列 mid 上的 n-1 个圆盘放到 accept 。当然调用我们的hanoi 。hanoi 的功能是将第一列上的圆盘放到第三列,所以,我们要将mid放到第一列,accept 放到第三列。代码如下

hanoi(mid, afford, accept, n - 1)

执行完成后,是这样的,结束。

最后放一个不一样的参数位置,以及代码实现

steps = 0


def hanoi(des, src, mid, n):  # des 目的列   src 最初的圆盘列    mid  辅助列
    global steps
    if n == 1:
        steps = steps + 1
        print("[STEP{:>4}] {}->{}".format(steps, src, des))
    else:
        hanoi(mid, src, des, n - 1)
        steps = steps + 1
        print("[STEP{:>4}] {}->{}".format(steps, src, des))
        hanoi(des, mid, src, n - 1)


hanoi('C', 'A', 'B', 3)  # 以3阶汉诺塔为例,C:目的列,A:最初圆盘列,B:辅助列

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值