下面的代码(不是我的,只研究它)在原始列表(即list_
)的递归和合并例程之间(正确地)反弹。堆栈帧的流程(即,他们如何以及为什么以他们的方式返回的方式还不清楚,即使在Python Tutor中观看,这是我在下面重述的内容)。有关代码如何返回以及问题如何遵循程序的说明。
def merge(left, right):
if not len(left) or not len(right):
return left or right
result = []
i, j = 0, 0
while (len(result) < len(left) + len(right)):
if left[i] < right[j]:
result.append(left[i])
i+= 1
else:
result.append(right[j])
j+= 1
if i == len(left) or j == len(right):
result.extend(left[i:] or right[j:])
break
return result
def mergesort(list_):
if len(list_) < 2:
return list_
middle = len(list_)//2
left = mergesort(list_[:middle])
right = mergesort(list_[middle:])
return merge(left, right)
list_ = [7,5,2,1,3]
print(mergesort(list_))
我们点击的第一个递归调用是left
(我们想要对列表的左半部分进行排序,然后对左半部分的左半部分进行排序等等,直到我们找到一个大小为1的列表,打基础案例)。这很好用。我们从[7,5,2,1,3]到[7,5]到[7]并且打了基本案例。到现在为止还挺好。 我希望堆栈帧开始返回,但事实并非如此。我认为它返回7,但随后我们跳转到right
的下一组递归调用。 5再次出现。 5紧接在7之前出现在堆栈框架中,因此事情以相反的顺序返回(这很好)。新参数拆分5并创建一个列表,从而设置基本情况并返回5.
这里变得奇怪:程序进入合并步骤,这是正确的。 如何&#34;知道&#34;跳过right
或left
的进一步递归(我给了它一个完整的列表,其中大部分未被触及)并跳转到合并?此外,它如何知道从合并中返回mergesort函数,没有明确的指令,并确切知道在哪里拿起?任何人都可以对此有所了解吗?传统的算法文本和大多数视频都没有帮助,因为它们不能解决堆栈帧问题。
答案 0 :(得分:1)
我认为你的困惑与不了解每个堆栈帧都有它自己的执行点有关。每次调用一个函数时,帧的执行暂停,并为函数调用创建一个新帧。当它返回时,前一帧从它停止的地方开始。没有中心位置“知道”接下来的代码应该去哪里,每个级别为自己处理。
在您描述的示例方案中,mergesort
对[7, 5]
的调用首先将列表拆分为[7]
和[5]
,然后依次对每个列表进行递归得到left
和right
。当第二个递归调用返回时,它会继续执行代码的下一部分,即merge
调用,因为这是代码中接下来的内容。
你可以清楚地看到逻辑,就在这里:
left = mergesort(list_[:middle])
right = mergesort(list_[middle:])
return merge(left, right)
这告诉你,在这次运行中(以及没有达到基本情况的每次运行),它将递归两次,然后merge
。