详细的实现
乍一看,您似乎必须区分可以直接从当前源移动到当前目标的情况,以及必须分两步移动最大磁盘的情况。以下实现可以做到这一点:
class Stack(object):
def __init__(self, index, name, disks):
self.index = index
self.name = name
self.disks = disks
def __str__(self):
return self.name
def __repr__(self):
return 'Stack(%r, %r, %r)' % (self.index, self.name, self.disks)
def is_adjacent(self, other):
return other.index in (self.index + 1, self.index - 1)
def push(self, disk):
assert len(self.disks) == 0 or self.disks[-1] > disk
self.disks.append(disk)
def pop(self):
return self.disks.pop()
class Hanoi(object):
def __init__(self, n):
source = Stack(0, "source", range(n, 0, -1))
helper = Stack(1, "helper", [])
target = Stack(2, "target", [])
self.stacks = [source, helper, target]
self.hanoi(n, source, target)
def hanoi(self, n, source, target):
"""Move n disks from source to target using remaining stack"""
helper = self.stacks[3 - source.index - target.index]
if n == 0:
return
if source.is_adjacent(target):
self.hanoi(n - 1, source, helper)
self.move(source, target)
self.hanoi(n - 1, helper, target)
else:
assert helper.is_adjacent(source) and helper.is_adjacent(target)
self.hanoi(n - 1, source, target)
self.move(source, helper)
self.hanoi(n - 1, target, source)
self.move(helper, target)
self.hanoi(n - 1, source, target)
def move(self, s, d):
assert s.is_adjacent(d)
disk = s.pop()
print "moving %d from %s to %s" % (disk, s, d)
d.push(disk)
Hanoi(5)
该Stack
对象有助于将有关您的一个堆栈的所有内容保持在一起:名称、位置及其相邻性以及当前的磁盘序列。如果添加第三个元素来保存该索引,则可以使用元组,但我认为 OOP 更直观。
该类Hanoi
将一组堆栈放在一起。这允许该hanoi
方法仅指定源和目标,同时推断第三个堆栈。您可以用不同的方式编写代码,但我发现省略帮助程序会使这些递归调用更容易理解:现在对于单磁盘move
和多磁盘hanoi
,您都指定源和目标,而不是第三个堆栈。
简化空间
现在,如果您仔细观察,您会发现递归调用总是针对不相邻的堆栈。因此,如果您的堆栈顺序确实是源和目标不相邻,那么所有递归调用都将使用else
我的代码的分支,并且您可以缩短内容以避免区分大小写并始终使用该else
分支。不过,就正在发生的事情而言,我发现上面更冗长的代码更容易理解。