Python 尝试用数组实现递归Hanoi塔算法

Python 尝试用数组实现递归Hanoi塔算法,python,arrays,algorithm,python-3.x,recursion,Python,Arrays,Algorithm,Python 3.x,Recursion,尽管这里有很多关于这个问题的问题,但没有一个能帮我解决这个问题。我知道什么是递归,我可以自己用2^n-1步轻松地解出《河内之塔》,但我在用Python编写算法时遇到了麻烦。基本情况是可行的,但我似乎找不到一种方法将“将n-1个磁盘移动到辅助peg,然后将最大的磁盘移动到目标peg”转换为数组操作,而且我不理解为什么在递归调用中弹出最后一个元素时,最后一个元素没有从数组中移除 以下是节目: peg_a = [1,0] peg_b = [] peg_c = [] def hanoi(start,a

尽管这里有很多关于这个问题的问题,但没有一个能帮我解决这个问题。我知道什么是递归,我可以自己用2^n-1步轻松地解出《河内之塔》,但我在用Python编写算法时遇到了麻烦。基本情况是可行的,但我似乎找不到一种方法将“将n-1个磁盘移动到辅助peg,然后将最大的磁盘移动到目标peg”转换为数组操作,而且我不理解为什么在递归调用中弹出最后一个元素时,最后一个元素没有从数组中移除

以下是节目:

peg_a = [1,0]
peg_b = []
peg_c = []

def hanoi(start,aux,target):
    print(start,aux,target)
    if len(start) == 1:
        target.append(start.pop())
        print(start,aux,target)
    else:
        hanoi(start[1:],target,aux)
        target.append(start.pop())
        print(start,aux,target)

hanoi(peg_a, peg_b, peg_c)
这就是印刷的内容:

[1, 0] [] []
[0] [] []
[] [] [0]
[1] [0] [0]

有什么帮助吗?

我认为一个问题是函数没有返回任何内容。您可以使用列表来保存big的内容,big是可修改的对象,因此可以将函数参数视为指向这些对象的指针。这可能会起作用,但问题是,通过使用
start[1://code>创建一个切片,可以创建一个新列表,因此它不再是指向原始列表的“指针”

一种解决方法可能是仍然使用输入参数作为列表的指针,而不是添加一些额外的整数函数参数,这些参数指示要移动多少磁盘

我会这样做:

def hanoi(pegs, start, target, n):
    assert len(pegs[start]) >= n, 'not enough disks on peg'
    if n == 1:
        pegs[target].append(pegs[start].pop())
        print '%i -> %i: %s' % (start, target, pegs)
    else:
        aux = 3 - start - target  # start + target + aux = 3
        hanoi(pegs, start, aux, n-1)
        hanoi(pegs, start, target, 1)
        hanoi(pegs, aux, target, n-1)
我不使用3个不同的列表,因为在您的代码中它们被交换,所以很难想象发生了什么。相反,我有一个
pegs
变量,它是一个列表列表。在我的例子中,
start
target
是我交换的挂钩指数。好的是,您现在可以打印各个步骤。快速演示:

pegs = [[4, 3, 2, 1], [], []]
hanoi(pegs, 0, 1, 4)    
结果

0 -> 2: [[4, 3, 2], [], [1]]
0 -> 1: [[4, 3], [2], [1]]
2 -> 1: [[4, 3], [2, 1], []]
0 -> 2: [[4], [2, 1], [3]]
1 -> 0: [[4, 1], [2], [3]]
1 -> 2: [[4, 1], [], [3, 2]]
0 -> 2: [[4], [], [3, 2, 1]]
0 -> 1: [[], [4], [3, 2, 1]]
2 -> 1: [[], [4, 1], [3, 2]]
2 -> 0: [[2], [4, 1], [3]]
1 -> 0: [[2, 1], [4], [3]]
2 -> 1: [[2, 1], [4, 3], []]
0 -> 2: [[2], [4, 3], [1]]
0 -> 1: [[], [4, 3, 2], [1]]
2 -> 1: [[], [4, 3, 2, 1], []]

您的代码有两个问题:

  • 虽然一开始这似乎是一个聪明的想法,但使用
    start[1://code>不起作用,因为您正在创建列表的副本,因此不再修改原始列表(请参见Bas的答案)
  • 您错过了
    else
    部分中的第二个递归调用,将光盘从
    aux
    peg堆叠到
    target
    peg
要解决第一个问题,最简单的方法是添加一个传统参数,指示要从
start
重新定位到
target
的光盘数量:

def hanoi(n, start, aux, target):
    if n == 1:
        target.append(start.pop())
    else:
        hanoi(n - 1, start, target, aux)
        target.append(start.pop())
        hanoi(n - 1, aux, start, target)
或更短:

def hanoi(n, start, aux, target):
    if n > 0:
        hanoi(n - 1, start, target, aux)
        target.append(start.pop())
        hanoi(n - 1, aux, start, target)

哦,这很有道理!我想这也解释了为什么某些元素在数组的最终状态下出现两次。是否可以通过将
start[1://code>分配给
else
块中的
start
来修复该问题,或者这会导致函数无法处理副本?它还缺少
else
部分中的第二个递归调用。该代码工作正常,将peg作为嵌套列表是一个聪明的想法,我大体上理解它的作用,但我发现很难想象n对每个调用的作用。你能解释一下吗?参数
n
就是你想移动的磁盘数量。递归技巧很简单:1)以任何方式将
n-1
磁盘移动到辅助销钉2)移动底部3)以任何方式将n-1个磁盘从辅助销钉移动到目标。“以任何方式”都是通过递归调用完成的。这个算法是有效的,因为你用大小
n-1
在子问题中解决了大小
n
的问题,当你实际移动一个磁盘时,这个问题最终可以在
n=1
的基本情况下解决。网上有很多更好的解释,也可以看到这个