替代合并排序的效率?

时间:2018-10-31 08:48:45

标签: python algorithm performance sorting data-structures

我正在学习合并排序,我已经看到很多教程都是通过替换原始数组的值来进行合并的,例如here。我想知道我的替代实现是否正确。我只看到1 tutorial做同样的事情。我的实现返回排序后的数组,如下所示:

def mergesort(arr):
    if len(arr) == 1:
        return arr
    mid = len(arr) // 2
    left_arr = arr[:mid]
    right_arr = arr[mid:]

    return merge(mergesort(left_arr), mergesort(right_arr))

def merge(left_arr, right_arr):
    merged_arr = [] # put merge of left_arr & right_arr here
    i,j = 0, 0 # indices for left_arr & right_arr

    while i < len(left_arr) and j < len(right_arr):
        if left_arr[i] < right_arr[j]:
            merged_arr.append(left_arr[i])
            i += 1
        else:
            merged_arr.append(right_arr[j])
            j += 1

    # add remaining elements to resulting arrray
    merged_arr.extend(left_arr[i:]) 
    merged_arr.extend(right_arr[j:])
    return merged_arr


arr = [12, 11, 13, 5, 6, 7]
sorted_arr = mergesort(arr)
print(sorted_arr)
# Output: [5, 6, 7, 11, 12, 13]

对我来说,这是一种进行合并排序的更直观的方法。此实现是否破坏了合并的类型?是速度效率还是空间效率(创建结果数组除外)效率较低?

4 个答案:

答案 0 :(得分:2)

如果我们正在考虑使用O(n)额外的内存进行合并排序,那么您的实现似乎是正确的,但是效率很低。让我们看一下这些行:

def mergesort(arr):
    ...
    mid = len(arr) // 2
    left_arr = arr[:mid]
    right_arr = arr[mid:]

您实际上是在每次调用mergesort()时创建两个新数组,然后从原始arr复制元素。这是堆和O(n)副本上的两个额外的内存分配。通常,由于复杂的分配器算法,堆内存分配非常慢。

老爸,让我们考虑一下这一行:

merged_arr.append(left_arr[i])  # or similar merged_arr.append(left_arr[j])

由于使用了动态分配的数组(又名列表),因此又发生了一堆内存分配。

因此,最有效的mergesort方法是在开始时分配一次额外的原始数组大小数组,然后将其部分用于临时结果。

def mergesort(arr):
    mergesort_helper(arr[:], arr, 0, len(arr))

def mergesort_helper(arr, aux, l, r):
    """ sorts from arr to aux """
    if l >= r - 1:
        return

    m = l + (r - l) // 2
    mergesort_helper(aux, arr, l, m)
    mergesort_helper(aux, arr, m, r)
    merge(arr, aux, l, m, r)

def merge(arr, aux, l, m, r):
    i = l
    j = m
    k = l
    while i < m and j < r:
        if arr[i] < arr[j]:
            aux[k] = arr[i]
            i += 1
        else:
            aux[k] = arr[j]
            j += 1
        k += 1

    while i < m:
        aux[k] = arr[i]
        i += 1
        k += 1

    while j < r:
        aux[k] = arr[j]
        j += 1
        k += 1

import random

def testit():
    for _ in range(1000):
        n = random.randint(1, 1000)
        arr = [0]*n
        for i in range(n):
            arr[i] = random.randint(0, 100)

        sarr = sorted(arr)
        mergesort(arr)
        assert sarr == arr

testit()

答案 1 :(得分:1)

Python家伙是否会为清单的有效性而烦恼:)?

要达到经典合并排序的最佳速度,在编译语言中,应仅提供一次辅助存储块以最大程度地减少分配操作(内存吞吐量通常是算术相当简单的限制阶段)。

也许这种方法(将工作空间预先分配为大小=源大小的列表)可能在Python实现中也很有用。

答案 2 :(得分:1)

您的归类排序实现是正确的。

正如您所指出的,您正在使用一个额外的数组来合并结果。使用此替代数组,会增加 O(n)的空间复杂度。

但是,您提到的第一个链接:VBA: UsedRange Does not update correctly 也增加了相同的空间复杂度:

/* create temp arrays */
int L[n1], R[n2]; 

注意:如果您有兴趣,请看看https://www.geeksforgeeks.org/merge-sort/

答案 3 :(得分:1)

我认为这是合并排序的良好实现,因为评估算法的复杂度是合并排序复杂度的一部分,即:给定n个要排序的元素数,

T(n) = 2T (n / 2) + n