Python中的递归合并排序和堆栈帧

时间:2017-05-19 01:32:41

标签: python recursion merge mergesort

下面的代码(不是我的,只研究它)在原始列表(即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;跳过rightleft的进一步递归(我给了它一个完整的列表,其中大部分未被触及)并跳转到合并?此外,它如何知道从合并中返回mergesort函数,没有明确的指令,并确切知道在哪里拿起?任何人都可以对此有所了解吗?传统的算法文本和大多数视频都没有帮助,因为它们不能解决堆栈帧问题。

1 个答案:

答案 0 :(得分:1)

我认为你的困惑与不了解每个堆栈帧都有它自己的执行点有关。每次调用一个函数时,帧的执行暂停,并为函数调用创建一个新帧。当它返回时,前一帧从它停止的地方开始。没有中心位置“知道”接下来的代码应该去哪里,每个级别为自己处理。

在您描述的示例方案中,mergesort[7, 5]的调用首先将列表拆分为[7][5],然后依次对每个列表进行递归得到leftright。当第二个递归调用返回时,它会继续执行代码的下一部分,即merge调用,因为这是代码中接下来的内容。

你可以清楚地看到逻辑,就在这里:

left = mergesort(list_[:middle])
right = mergesort(list_[middle:])

return merge(left, right)

这告诉你,在这次运行中(以及没有达到基本情况的每次运行),它将递归两次,然后merge