簡體   English   中英

Python遞歸合並按索引排序

[英]Python recursive Merge Sort by index

我有一個關於遞歸合並排序的 Python 版本的問題。 我完成了僅由數組引用的基本版本,現在正在研究索引版本。 我會陷入無限循環,但我不確定我哪里做錯了。 你介意分享一些想法嗎? 先感謝您。

成功和非索引版本:

def mergesort(x):
    # The base case is when the array contains less than 1 observation. 
    length = len(x)
    if length < 2:
        return x
    else:
        # Recursive case:merge sort on the lower subarray, and the upper subarray. 
        mid = (length) // 2
        lower = mergesort(x[:mid])
        upper = mergesort(x[mid:])
        # merge two subarrays.
        x_sorted = merge(lower, upper)
        return (x_sorted)

def merge(lower, upper):
    nlower = len(lower)
    nupper = len(upper)
    i, j, k = 0, 0, 0
    # create a temp array to store the sorted results
    temp = [0] * (nlower + nupper)
    # as the lower and upper are sorted, since the base case is the single observation. 
    # now we compare the smallest element in each sorted array, and store the smaller one to the temp array
    # repeat this process until one array is completed moved to the temp array 
    # store the other array to the end of the temp array 
    while i < nlower and j < nupper:
        if lower[i] <= upper[j]:
            temp[k] = lower[i]
            i += 1
            k += 1
        else:
            temp[k] = upper[j]
            j += 1
            k += 1
    if i == nlower:
        temp[i+j:] = upper[j:]
    else:
        temp[i+j:] = lower[i:]
    return temp 

預期結果:

x = random.sample(range(0, 30), 15)
mergesort(x)
[0, 1, 3, 6, 9, 10, 11, 13, 14, 17, 18, 20, 25, 27, 29]

但是我會陷入使用索引版本的無限循環:

def ms(x, left, right):
    # the base case: right == left as a single-element array
    if left < right:
        mid = (left + right) // 2
        ms(x, left, mid)
        ms(x, mid, right + 1)
        m(x, left, mid, right)
    return m
def m(x, left, mid, right):
    nlower = mid - left
    nupper = right - mid + 1
    temp = [0] * (nlower + nupper)
    ilower, iupper, k = left, mid, 0
    
    while ilower < mid and iupper < right + 1:
        if x[ilower] <= x[iupper]:
            temp[k] = x[ilower]
            ilower += 1
            k += 1
        else:
            temp[k] = x[iupper]
            iupper += 1
            k += 1
    if ilower == mid:
        temp[k:] = x[iupper:right+1]
    else:
        temp[k:] = x[ilower:mid]
    x[left:right+1] = temp
    return x

測試數據為:

x = random.sample(range(0, 30), 15)
ms(x, 0, 14)
---------------------------------------------------------------------------
RecursionError                            Traceback (most recent call last)
<ipython-input-59-39859c9eae4a> in <module>
      1 x = random.sample(range(0, 30), 15)
----> 2 ms(x, 0, 14)

... last 2 frames repeated, from the frame below ...

<ipython-input-57-854342dcdefb> in ms(x, left, right)
      3     if left < right:
      4         mid = (left + right)//2
----> 5         ms(x, left, mid)
      6         ms(x, mid, right+1)
      7         m(x, left, mid, right)

RecursionError: maximum recursion depth exceeded in comparison

你介意提供一些見解嗎? 謝謝。

您的索引版本使用了一種令人困惑的約定,即left是切片中第一個元素的索引, right是最后一個元素的索引。 此約定需要容易出錯的+1 / -1調整。 你的問題是這樣的: mid as計算是左半部分最后一個元素的索引,但你認為mid是右半部分的第一個元素:2個元素的切片被分成一個0和一個2,因此無限遞歸。 您可以通過將ms(x, mid, right+1)更改為ms(x, mid+1, right)等來解決此問題。

此外,從函數ms重新調整m沒有意義。 如果有的話,您應該返回x

就像 Python 中的范圍說明符一樣,作為最后一個元素之后的索引的right更不容易出錯。 這樣就不會出現令人困惑的+1 / -1調整。

這是修改后的版本:

def ms(x, left, right):
    # the base case: right - left as a single-element array
    if right - left >= 2:
        mid = (left + right) // 2  # index of the first element of the right half
        ms(x, left, mid)
        ms(x, mid, right)
        m(x, left, mid, right)
    return x

def m(x, left, mid, right):
    nlower = mid - left
    nupper = right - mid
    temp = [0] * (nlower + nupper)
    ilower, iupper, k = left, mid, 0
    
    while ilower < mid and iupper < right:
        if x[ilower] <= x[iupper]:
            temp[k] = x[ilower]
            ilower += 1
            k += 1
        else:
            temp[k] = x[iupper]
            iupper += 1
            k += 1
    if ilower == mid:
        temp[k:] = x[iupper:right]
    else:
        temp[k:] = x[ilower:mid]
    x[left:right] = temp
    return x

您將調用為:

x = random.sample(range(0, 30), 15)
ms(x, 0, len(x))

暫無
暫無

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

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