【问题标题】:Quicksort: Non-in-place implementation works. In-place implementation exceeds maximum recursion depth. Why?快速排序:非就地实现有效。就地实现超过最大递归深度。为什么?
【发布时间】:2016-11-05 21:42:33
【问题描述】:
def quicksort_stable(l):
    if not l:
        return l
    else:
        pivot = l[0]
        return quicksort_stable([x for x in l if x < pivot]) \
                + [x for x in l if x == pivot] \
                + quicksort_stable([x for x in l if x > pivot])    

def quicksort_inplace(l):
    def partition(start_idx, end_idx):
        left_idx = start_idx + 1
        right_idx = end_idx
        while True:
            while left_idx <= right_idx and l[left_idx] <= l[start_idx]:
                left_idx += 1
            while right_idx >= left_idx and l[right_idx] >= l[start_idx]:
                right_idx -= 1

            if right_idx < left_idx:
                break
            else:
                l[left_idx], l[right_idx] = l[right_idx], l[left_idx]

        l[start_idx], l[right_idx] = l[right_idx], l[start_idx]     

        return right_idx

    def qs(start_idx, end_idx):
        if start_idx < end_idx:
            split_idx = partition(start_idx, end_idx)
            qs(start_idx, split_idx - 1)
            qs(split_idx + 1, end_idx)

    qs(0, len(l) - 1)
    return l

if __name__ == '__main__':

    import random

    l1 = [random.randint(0, 9) for x in range(10000)]
    l2 = [x for x in l1]

    l1 = quicksort_stable(l1)
    quicksort_inplace(l2)

我故意选择第一个元素作为枢轴而不是随机化,以确保两个实现的行为方式相同。

这两种实现都是递归实现的。在调用堆栈中,似乎 quicksort_inplace 应该占用 O(lg n) 空间,而 quicksort_stable 应该占用 O(n) 空间,因为它每次递归都会创建一个新列表。

然而,quicksort_inplace 是导致“超出最大递归深度”的原因,而 quicksort_stable 工作正常。

为什么会这样?

【问题讨论】:

  • 尝试在qs中打印start_idx和end_idx的值,这应该会给你一个线索。
  • 呵呵,是不是因为小范围的数字导致 quicksort_inplace 每次递归时都会在部分排序的列表上递归,而部分排序的列表对快速排序算法不利? quicksort_stable 通过收集所有等于枢轴的元素来解决这个问题,并且不会递归。

标签: python recursion stack-overflow quicksort


【解决方案1】:

我相信这种行为的原因是您的列表包含大量重复(每个元素出现约 1000 次),并且您通过收集所有等于 pivot 的元素来“欺骗”实现 stable 版本立即而不是回复他们(这当然很棒!)。

所以要实际比较这两个过程,它应该如下所示:

    def quicksort_stable(l):
        if not l or len(l)==1:
            return l
        else:
            pivot = l[0]
            rst = l[1:]
            return quicksort_stable([x for x in rst if x < pivot]) \
                    + [pivot] \
                    + quicksort_stable([x for x in rst if x >= pivot])    

另外,为了获得上述破坏性(就地)版本,您应该将第二个 while 的条件更改为 大于(以便在 right_idx em>有不少于pivot的元素),即

        while right_idx >= left_idx and l[right_idx] > l[start_idx]:

如果您这样做,您会发现这两个过程都会导致具有来自 range(0,9) 的 10000 个元素的数组发生堆栈溢出(另请注意,使用 range(0,99) 情况并非如此,因为它们需要更少的“削减”)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-10-05
    • 2017-09-04
    • 1970-01-01
    • 1970-01-01
    • 2021-04-09
    • 2011-11-05
    • 1970-01-01
    相关资源
    最近更新 更多