当我读到排序算法的解释时,我想,“光看我记不住”。
我们实现了 7 种代表性算法,并通过实际运行它们来比较性能。
我觉得用Python写会很容易,所以我用Python写了

目录

源代码

冒泡排序

  • 对于所有元素,与相邻元素进行比较,如果顺序颠倒则替换。通过对元素数-1次重复此排序
  • 最简单的排序
平均计算时间 最坏情况计算时间 内存使用情况 稳定
$n^{2}$ $n^{2}$ $1$
def bubble_sort(lst):
    n = len(lst)
    for i in range(n):
        for j in range(n-1):
            if lst[j] >= lst[j+1]:
                lst[j], lst[j+1] = lst[j+1], lst[j]
    return lst

选择排序

  • 找到具有最低值的元素并将其与第一个元素交换(排序到第一个元素)同样在
  • 之后,找到未排序部分的最小元素,并将其替换为未排序部分的第一个元素
  • 当所有元素都排序后结束处理
平均计算时间 最坏情况计算时间 内存使用情况 稳定
$n^{2}$ $n^{2}$ $1$ 没有任何
def selection_sort(lst):
    n = len(lst)
    for i in range(0, n-1):
        min = i
        for j in range(i+1, n):
            if lst[j] < lst[min]:
                min = j
        lst[i], lst[min] = lst[min], lst[i] 
    return lst

插入排序

  • 比较第 0 和第 1 个元素,如果顺序颠倒,则交换它们
  • 如果第二个元素小于第一个元素,则以正确的顺序“插入”它(对于数组,将前一个元素向后移动一个)
  • 对第三个及以后的元素继续相同的过程,当所有元素都排序后结束该过程
平均计算时间 最坏情况计算时间 内存使用情况 稳定
$n^{2}$ $n^{2}$ $1$
def insertion_sort(lst):
    for i in range(1, len(lst)):
        tmp = lst[i]
        j = i - 1

        while j >= 0 and lst[j] > tmp:
            lst[j+1] = lst[j]
            j -= 1
        lst[j+1] = tmp
    return lst

壳排序

  • 确定合适的区间 h
  • 对以间隔 h 获取的数据列应用插入排序
  • 缩小区间 h 并重复提取数据列和应用插入排序的过程,直到 h 变为 1
平均计算时间 最坏情况计算时间 内存使用情况 稳定
$nlog n$ $geqqn^{1.5}$ $1$ 没有任何
def shell_sort(lst):
    n = len(lst)
    h = 1
    while h < n / 9:
        h = h * 3 + 1
    while h > 0:
        for i in range(h, n):
            j = i
            while j >= h and lst[j-h] > lst[j]:
                lst[j], lst[j-h] = lst[j-h], lst[j]
                j -= h
        h = h // 3
    return lst

快速排序

  • 选择名为 Pivot 的边界值
  • 在数组开头收集枢轴下方的元素,并将仅包含枢轴下方元素的部分与其余部分分开
  • 为分割的部分再次选择并分割枢轴。重复此过程,直到对分割的部分进行排序
  • 最后合并分割区间
平均计算时间 最坏情况计算时间 内存使用情况 稳定
$nlog n$ $n^2$ $nlog n$ 没有任何
def quick_sort(lst):
    if len(lst) <= 1:
        return lst

    pivot = lst[0]
    left = []
    center = []
    right = []

    for v in lst:
        if v < pivot:
            left.append(v)
        elif v > pivot:
            right.append(v)
        else:
            center.append(v)
    
    left = quick_sort(left)
    right = quick_sort(right)

    return left + center + right

堆排序

  • 从未对齐的数据字符串中删除元素并将它们按顺序添加到堆中。重复直到添加所有元素
  • 取根(最大值或最小值)并将其添加到排序列表中。重复直到检索到所有元素
平均计算时间 最坏情况计算时间 内存使用情况 稳定
$nlog n$ $nlog n$ $1$ 没有任何
def heap_sort(lst):
    n = len(lst)
    for i in range(n//2-1, -1, -1):
        heapify(lst, i, n)

    for i in range(n-1, 0, -1):
        lst[0], lst[i] = lst[i], lst[0]
        heapify(lst, 0, i)
        
    return lst

def heapify(lst, i, heap_size):
    largest = i
    left = 2 * i + 1
    right = 2 * i + 2

    if left < heap_size and lst[left] > lst[largest]:
        largest = left

    if right < heap_size and lst[right] > lst[largest]:
        largest = right

    if largest != i:
        lst[largest], lst[i] = lst[i], lst[largest]
        heapify(lst, largest, heap_size)

归并排序

  • 拆分(通常是两半)数据列
  • 如果拆分数据列有1条数据,则返回,如果有2条或更多,则进一步拆分数据列。
  • 对上述递归返回的数据列做简单的元素比较排序
  • 合并两个已排序的数据列(如果是一个,则为自身)
平均计算时间 最坏情况计算时间 内存使用情况 稳定
$nlog n$ $nlog n$ $n$
def merge_sort(lst):
    if len(lst) <= 1:
        return lst

    mid = len(lst) // 2
    left = lst[:mid]
    right = lst[mid:]

    left = merge_sort(left)
    right = merge_sort(right)

    return merge(left, right)

def merge(left, right):
    merged = []
    left_i, right_i = 0, 0

    while left_i < len(left) and right_i < len(right):
        if left[left_i] <= right[right_i]:
            merged.append(left[left_i])
            left_i += 1
        else:
            merged.append(right[right_i])
            right_i += 1

    if left_i < len(left):
        merged.extend(left[left_i:])
    if right_i < len(right):
        merged.extend(right[right_i:])
    
    return merged

我比较了执行时间

我尝试使用每种算法从 -100,000 到 100,000 范围内随机选择 100,000 个数字
我对三个数据集进行了排序,并检查了每个会话所需的时间和平均所需的时间。

算法 所需时间(秒) 所需时间(秒) 所需时间(秒) 平均所需时间(秒)
冒泡排序 706.051 722.234 727.029 718.438
选择排序 200.039 204.399 200.981 201.806
插入排序 203.928 202.049 203.315 203.097
壳排序 0.516 0.517 0.557 0.530
快速排序 0.146 0.132 0.139 0.139
堆排序 0.481 0.433 0.473 0.462
合并排序 0.287 0.271 0.279 0.279

同样,基本排序冒泡/选择/插入和加速排序外壳/快速/堆/合并之间存在显着的性能差异。

在加速排序中,快速排序是最快的
(不过,由于我这次使用的是相对稳定的数据集,所以在最坏的情况下查看可能会很慢。)
亚军,归并排序也相当快

顺便说一下,每个要排序的对象数量所需的时间如下表所示。

算法 1,000 10,000 100,000
冒泡排序 0.073 6.984 718.438
选择排序 0.019 1.976 201.806
插入排序 0.020 2.046 203.097
壳排序 0.002 0.030 0.530
快速排序 0.001 0.012 0.139
堆排序 0.002 0.034 0.462
合并排序 0.002 0.023 0.279

随着数量的增加,计算复杂度为 $O(n^{2})$ 的算法会显着增加处理秒数,
复杂度为 $O(nlogn)$ 的算法在几秒钟内保持持续增长

奖金

从上面的实现派生出来,python标准提供的排序函数是
我很好奇它是如何实现的,所以我查了一下。

显然,python的排序功能是时间排序它似乎使用了一种称为

性能如下

平均计算时间 最坏情况计算时间 内存使用情况 稳定
$nlog n$ $nlog n$ $n$

当我实际运行它时,执行时间如下

算法 1,000 10,000 100,000
timsort(python标准) 0.000 0.001 0.013

相当快! (为了能够以这次实施的快速排序速度的 1/10 进行排序......)
感觉是标准实现

即使从理论性能来看,最坏情况的计算时间也比快速排序(quicksort: $O(n^{2})$, timsort: $O(nlogn)$)快很多,所以要考虑内存使用情况。如果你不不做,我觉得这是一个很不错的算法

实施地点大概是

如果我足够努力,也许能看懂,但由于是用C语言写的(经验不足),看不懂,所以这次放弃了……
(总有一天,我也想了解 Timsort 的实现......)

因此,我将在本文中不再赘述。
如果您想了解更多有关 TimSort 的信息,请阅读以下页面!

参考页


原创声明:本文系作者授权爱码网发表,未经许可,不得转载;

原文地址:https://www.likecs.com/show-308632240.html

相关文章:

  • 2021-11-20
  • 2021-07-19
  • 2021-10-22
  • 2021-12-18
  • 2021-12-15
  • 2021-12-06
  • 2021-05-31
猜你喜欢
  • 2021-11-07
  • 2021-11-07
  • 2018-12-06
  • 2021-07-27
  • 2021-04-21
  • 2021-12-31
  • 2021-05-12
  • 2021-06-14
相关资源
相似解决方案