簡體   English   中英

從偽代碼 python 實現合並排序的問題

[英]Problem implementing Merge Sort from pseudo code python

我試圖基於以下偽代碼在 Python 中實現歸並排序。 我知道那里有很多實現,但是我找不到一個遵循這種模式的實現,最后是 for 循環而不是 while 循環。 此外,將子數組中的最后一個值設置為無窮大是我在其他實現中沒有看到的。 注意:以下偽代碼具有基於 1 的索引,即索引從 1 開始。所以我認為我最大的問題是正確建立索引。 現在它只是沒有正確排序,而且它真的很難用調試器來跟蹤。 我的實現在底部。

當前 Output:

Input:  [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
Merge Sort:  [0, 0, 0, 3, 0, 5, 5, 5, 8, 0]

在此處輸入圖像描述

在此處輸入圖像描述

def merge_sort(arr, p, r):
    if p < r:
        q = (p + (r - 1)) // 2
        merge_sort(arr, p, q)
        merge_sort(arr, q + 1, r)
        merge(arr, p, q, r)

def merge(A, p, q, r):
    n1 = q - p + 1
    n2 = r - q

    L = [0] * (n1 + 1)
    R = [0] * (n2 + 1)

    for i in range(0, n1):
        L[i] = A[p + i]

    for j in range(0, n2):
        R[j] = A[q + 1 + j]

    L[n1] = 10000000 #dont know how to do infinity for integers
    R[n2] = 10000000 #dont know how to do infinity for integers

    i = 0
    j = 0

    for k in range(p, r):
        if L[i] <= R[j]:
            A[k] = L[i]
            i += 1
        else:
            A[k] = R[j]
            j += 1

    return A

首先,您需要確定由pr表示的區間在其端點處是打開還是關閉。 偽代碼(for 循環包括最后一個索引)確定區間在兩個端點處都是閉合的:[ p, r ]。

考慮到最后的觀察,您可以注意到for k in range(p, r):不檢查最后一個數字,因此正確的行是for k in range(p, r + 1):

您可以通過使用 [ p, r ] 范圍內的A的最大元素加一來表示問題中的“無窮大”。 這將使工作完成。

您不需要返回數組A ,因為所有更改都是通過其引用完成的。

此外, q = (p + (r - 1)) // 2沒有錯(因為 p < r),但正確的方程式是q = (p + r) // 2作為您想要的區間,中間 integer 值為兩個數字。

這是使用“現代”約定對算法的重寫,如下所示:

  • 索引從 0 開始
  • 范圍的結尾不是該范圍的一部分; 換句話說,間隔在左邊是封閉的,在右邊是開放的。

這是生成的代碼:

INF = float('inf')

def merge_sort(A, p=0, r=None):
    if r is None:
        r = len(A)
    if r - p > 1:
        q = (p + r) // 2
        merge_sort(A, p, q)
        merge_sort(A, q, r)
        merge(A, p, q, r)

def merge(A, p, q, r):
    L = A[p:q]; L.append(INF)
    R = A[q:r]; R.append(INF)
    i = 0
    j = 0
    for k in range(p, r):
        if L[i] <= R[j]:
            A[k] = L[i]
            i += 1
        else:
            A[k] = R[j]
            j += 1

A = [433, 17, 585, 699, 942, 483, 235, 736, 629, 609]
merge_sort(A)
print(A)
# → [17, 235, 433, 483, 585, 609, 629, 699, 736, 942]

筆記:

  • Python 具有用於復制子范圍的便捷語法。
  • Python中沒有int infinity,但我們可以使用float一,因為int和float總是可以比較的。
  • 這個算法和原來的算法有一個區別,但無關緊要。 由於“中點” q不屬於左范圍,當它們的長度之和為奇數時, LR短。 在原始算法中, q屬於L ,因此在這種情況下L是兩者中較長的一個。 這不會改變算法的正確性,因為它只是交換了LR的角色。 如果由於某種原因您不需要這種差異,那么您必須像這樣計算q
        q = (p + r + 1) // 2

這里 i 和 j 是從 0 開始的索引。 暫時忽略代碼中的first_array和second_array。

請注意,i 和 j 定義了我當前正在處理的數組區域。 在數學中,我們用[i, j)表示所有大於或等於 i 且小於 j 的實數。 注意 [ 和 ) 括號的使用。
我以相同的方式使用 i 和 j 來表示我當前正在處理的區域。 數組的區域 [i, j) 覆蓋了該數組中大於或等於 i 且小於 j 的所有索引(整數值)。

更好地理解這一點的例子

如果您的區域跨越整個數組,那么 i 應該是 0 並且 j 應該是數組的長度
([0,長度),現在你知道為什么了)。

區域 [i, i + 1) 中只有索引 i。

區域 [i, i + 2) 中有索引 i 和 i + 1。

def mergeSort(first_array, second_array, i, j):
    if j > i + 1:
        mid = (i + j + 1) // 2
        mergeSort(second_array, first_array, i, mid)
        mergeSort(second_array, first_array, mid, j)
        merge(first_array, second_array, i, mid, j)

可以看到,我已經將中間點計算為mid = (i + j + 1) // 2或者也可以使用mid = (i + j) // 2兩者都可以。 我將使用這個mid值將我正在處理的數組的當前區域划分為 2 個較小的區域。
在代碼的第 4 行,在區域 [i, mid) 上調用MergeSort ,在第 5 行,在區域 [mid, j) 上調用MergeSort

您可以在此處訪問整個代碼。

暫無
暫無

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

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