问题描述

Hanoi塔问题是法国数学家Edouard Lucas在1883年提出的一个数学问题。该问题可描述如下:

有n个空心圆盘串在一根柱子上,不妨设为A柱,圆盘的大小从下往上依次减小,现在再给出另外两根柱子,设为B柱和C柱,请问如何将这些圆盘借助B柱从A柱上转移到C柱上?移动条件有两个,其一为每次只能移动一个圆盘,其二为每根柱子上的圆盘都必须保持上小下大。

解决方案

仅考虑只有两个圆盘的情况,首先把最上面的小盘移动到B柱上,再把A柱上的大盘转移到C柱上,最后把B柱上的小盘转移到C柱上即可完成任务,共需三步。现在考虑有N个圆盘的情况,完全可以按照上面的思路,将这N个圆盘切分成两部分,一部分是最下面的大盘,另一部分则为大盘上面的N-1个盘子,这样,我们就可以按照两个圆盘的情况,先把上面的N-1个小盘移动到B柱上,再把最下面的大盘转移到C柱上,最后把剩余的N-1个圆盘整体转移到C盘上,问题便得到了解决。但是每次只能移动一个圆盘啊,上面整体移动了N-1个圆盘是不能满足题目要求的,那怎么办?这时候再作切分,那N-1个圆盘可以划分成最下面的大盘和剩余的N-2个圆盘,继续按照上述移动方法来移动,那这里还是移动了多余一个圆盘了呀?那就再作切分,一直下去,直到只移动一个圆盘为止。所以可见,这里用的是递归的思想。对于递归的思考,不要陷入一层层的具体过程中去,只要把握住两点,一个是从整体上来考虑转移圆盘的方式;另一个是终止的条件,即圆盘的个数肯定是有限的,一层层地递减下去总会有个边界,当移动的圆盘减至为1的时候,便可以终止递归了。

代码实现

根据上述的思考,代码其实非常简单,这里给出Python版本:

def hanoi(n, start, middle, end):
    if (n == 1):
        print('Move a plate from ' + start + ' to ' + end)
    else:
        hanoi(n-1, start, end, middle)
        hanoi(1, start, middle, end)
        hanoi(n-1, middle, start, end)

假如有三个圆盘,则输出如下所示:

Move a plate from A to C
Move a plate from A to B
Move a plate from C to B
Move a plate from A to C
Move a plate from B to A
Move a plate from B to C
Move a plate from A to C

时间复杂度

还是以N个圆盘切分成上面N-1个圆盘和最下面的大盘这两部分为例,从上面的分析可知移动这两部分总共需要移动三次,即移动N-1个圆盘从A柱到B柱一次,最大的圆盘从A柱移动到C柱一次,最后把N-1个圆盘从B柱移动到C柱一次。那么这三次中,移动N-1个圆盘的次数为两次,移动最大的圆盘次数为一次。以T(N)表示移动N个圆盘所需次数,那么就有:

\[T(N) = 2 \times T(N-1) + 1 \tag{1} \]

同理对于T(N-1)个圆盘,如果切分成两部分来移动的话,也有类似于上面的式子:

\[T(N-1) = 2 \times T(N-2) + 1 \tag{2} \]

所以最终移动次数为:

\[\begin{align*} T(N) &= 2 \times T(N-1) + 1 \\ &= 2 \times (2 \times T(N-2) + 1) + 1 \\ &= ... \\ &=2^{N-1} + 2^{N-2} + ... + 1 \\ &= 2^N - 1 \end{align*} \tag{3} \]

综上,该算法的时间复杂度为\(O(2^N)\)

posted on 2018-05-22 23:00  艾克_塞伦特  阅读(417)  评论(0编辑  收藏  举报