我试图了解这个特殊的河内塔问题解决方案。这是一种递归解决方案。
A = [5, 4, 3, 2, 1]
B = []
C = []
def move(n, source, target, auxiliary, frm):
print('n here : ', n)
if n > 0:
print('frm : ', frm, n)
# move n - 1 disks from source to auxiliary, so they are out of the way
move(n - 1, source, auxiliary, target, '1')
# move the nth disk from source to target
target.append(source.pop())
print('n:', n)
# Display our progress
print(source, target, auxiliary, '##############', sep = '\n')
# move the n - 1 disks that we left on auxiliary onto target
move(n - 1, auxiliary, target, source, '2')
# initiate call from source A to target C with auxiliary B
move(5, A, C, B, '0')
你可以在这里看到它 https://repl.it/JUzY/0
在#########的第一行打印后,n = 0的值变为2.这是怎么发生的?我错过了一些递归的概念吗?帮助我理解这一点。
答案 0 :(得分:2)
您需要了解范围。你说" n = 0的值,但突然变成2"。但是有多个" n",因为n的范围是方法。每次调用move
时,该方法调用都有自己的n
实例,并带有自己的值。
在脚本底部,您拨打move(5, A, C, B, '0')
。在通话中,n == 5
。首先要做的是致电move(n - 1, source, auxiliary, target, '1')
。在中调用n == 4
。但是当它最终返回时,原始函数调用中的n
仍为5
。
理解递归如何工作的一个好方法是使用纸张手工完成程序执行。我将使用便利贴进行功能调用,并使用笔记本进行输出。您还可以使用列表A,B,C
的形式获得"模型"。由于元素四处移动,您可以用纸屑或拼字游戏瓷砖表示这些元素。
从初始move(4, A, C, B, '0')
开始。 (我从4开始,因为步骤数量迅速增长)。因为我们正在调用一个函数,所以使用一个新的post-it来表示函数调用,然后在其上写:
n = 4
source = A
target = C
auxiliary = B
frm = '0'
现在通过代码move
来完成它所说的内容。在此页面上写下任何print
输出。
当您接到move(n - 1, source, auxiliary, target, '1')
的来电时,请记下您所在行号的贴子,然后获取新的帖子,并写下要进入的值它:
n = 4 (because 4 - 1)
source = A
target = B (caller's auxiliary)
auxiliary = C (caller's target)
frm = '1'
将此放在之前的帖子上。在重新显示之前,您不得查看被遮盖的帖子。继续这样做,你最终会得到一个五个叠加后。
第五个帖子有n = 0
。现在,当您单步执行代码时,因为它以if n > 0:
开头,所以调用立即返回。这个函数调用结束了,所以撕掉一个post-it并把它扔掉。 post-it 中n
等的值对其他帖子没有影响。
您记下了您所在的行号,因此请从该行手动执行代码。您将产生一些输出,然后再次调用move
- 使用post-it进行此移动,再次覆盖当前的post-it并增加堆栈直到您使用n == 0
进行调用并且可以从堆栈中删除一个项目。
如果你继续这样做,你会看到堆栈增长和缩小几次,你会到达原始的帖子一次,进行打印,然后再次增长堆栈,然后达到原始状态再次发布,最后完成。
便利贴的堆栈是程序运行时的执行堆栈的精确模型。当您看到堆栈跟踪(甚至是非递归程序)时,这就是它们所代表的内容。每个post-it是堆栈帧,并且局部变量的范围仅限于该堆栈帧。
河内的递归解决方案可以用英语表示: