簡體   English   中英

快速排序:非就地實施工作。 就地實現超出了最大遞歸深度。 為什么?

[英]Quicksort: Non-in-place implementation works. In-place implementation exceeds maximum recursion depth. Why?

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可以正常工作。

為什么會這樣呢?

我相信這種行為的原因是您的列表包含很多重復(每個元素出現〜1000次),並且您“欺騙”了實現穩定版本,方法是立即收集所有等於樞軸的元素,而不是返回它們(當然很棒!)。

因此,要實際比較這兩個過程,它應如下所示:

    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])    

此外,為了具有破壞性(就地)版本以上,你應該改變你的第二個條件,而以大於號 (以便在right_idx的右側有元素不超過支點以下),即

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

如果執行此操作,您會發現這兩個過程都會導致在范圍為(0,9)的 10000個元素的數組上發生堆棧溢出(還請注意,對於范圍為(0,99)的情況並非如此,因為它們需要較少的“剪切”) 。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM