欢迎来到我们的排序算法比较文章。在这里,我们将根据几个基本因素对各种排序算法进行比较。
- 时间复杂度
- 空间复杂度
- 稳定的/不稳定的
- 实际字段测试
我们将通过尝试描述每种算法最适合的地方,以及它们的强项和弱项来完成这一切。每种算法都是独一无二的,并在其特有的某些情况下表现得最好。
时间复杂度的比较
一个表格显示了一些最常用的排序算法的时间复杂度。在比较两种排序算法时,时间复杂度是你需要检查的第一件事。时间复杂度越低越好。
| 排序算法 | 平均情况 | 最佳案例 | 最差情况 |
|---|---|---|---|
| 泡沫排序 | O(n2) | O(n) | O(n2) |
| 插入式排序 | O(n2) | O(n) | O(n2) |
| 选择排序 | O(n2) | O(n2) | O(n2) |
| 快速排序 | O(n.log(n)) | O(n.log(n)) | O(n2) |
| 合并排序 | O(n.log(n)) | O(n.log(n)) | O(n.log(n)) |
| 堆排序 | O(n.log(n)) | O(n.log(n)) | O(n.log(n)) |
| 计数排序 | O(n+k) | O(n+k) | O(n+k) |
| 径向排序 | O(n*k) | O(n*k) | O(n*k) |
| 桶式排序 | O(n+k) | O(n+k) | O(n2) |
我们在上表中使用了一种颜色方案,以帮助我们进行排序算法的比较。红色是最差的,O(n2)算法位于其下。接下来是O(n.log(n))算法,这是一个中间地带。最好的时间复杂度是O(n),这是最快的算法。
以后我们做实际的现场测试时,你可以用这个表作为参考。你会发现时间复杂度对性能的影响有多大。
空间复杂度的比较
虽然速度很重要,而且通常是你的首要任务,但有时在有内存限制的地方,具有低内存成本的算法是首选。
下表显示了各种排序算法的空间复杂度。你可能会注意到,空间复杂度较高的算法是那些 "不在原地 "的算法,而空间复杂度最低的则是在原地的算法。当然,这是因为 "离位 "算法创建了额外的数组来存储数据,而 "就位 "算法则使用相同的数组。
不言而喻,最好的空间复杂度是O(1)。
| 排序算法 | 空间复杂度 |
|---|---|
| 泡沫排序 | O(1) |
| 插入式排序 | O(1) |
| 选择排序 | O(1) |
| 快速排序 | O(log(n)) |
| 合并排序 | O(n) |
| 堆排序 | O(1) |
| 计数排序 | O(k) |
| 矩阵排序 | O(n + k) |
| 桶式排序 | O(n) |
稳定的和不稳定的算法
这是一个相当小众的用途,只在某些类型的数据中产生实际的差异。然而,它仍然是这些特定场景所需要的一个重要要求。
| 排序算法 | 稳定的排序? |
|---|---|
| 泡沫排序 | 是的 |
| 插入式排序 | 是的 |
| 选择排序 | 没有 |
| 快速排序 | 没有 |
| 合并排序 | 有 |
| 堆积排序 | 没有 |
| 计数排序 | 有 |
| 径向排序 | 有 |
| 桶状分类 | 是的 |
然而,重要的是要注意,你通常可以创建上述算法的稳定版本。上图中提到的那些算法是 "经典 "版本的算法。
你也可以查看我们关于排序算法的YouTube系列。
排序算法--字段测试
最后,我们要测量的是主要部分,即性能。我们已经在各种情况下测试了这里的9种算法。从100个数字到10,000个数字,以及使用已经排序的数据进行测试,这些测试将揭示相当多的东西。
测试方法
我使用Google Collab来运行这些测试,以确保一个稳定和公平的测试环境。要明确的是,这些排序算法的代码是用Python写的,而且是以相当标准的方式写的。没有进行大规模的优化,而且只使用了标准(经典)版本的算法。
Pythontimeit和随机库被用来生成随机数据,并对每个算法进行连续和重复测试。这也是为了确保公平的结果。随机库用于生成从1到10,000的数字,timeit库总共进行了5次测试,并返回所有5次的列表。我们同时显示了5次测试的最大值和最小值,所以你可以看到次数的位移。
5次测试中的每一次实际上都是在运行代码10次,(数字参数,默认值为1,000,000)。通过做大量的测试并将数值相加来平均化,这就提高了准确性。如果你想要一个单独的排序的时间,请将最小/最大值除以10。重复次数是由重复参数控制的(默认值为5)。
你可以在下面的代码中看到测试功能的代码。如果你跟随timeit和随机库的链接,你可以了解更多关于这里发生的事情。
import random
import timeit
import sys
def test():
SETUP_CODE = '''
from __main__ import sort
from random import randint'''
TEST_CODE = '''
array = []
for x in range(1000):
array.append(x)
sort(array)
'''
times = timeit.repeat( setup = SETUP_CODE,
stmt = TEST_CODE,
number = 10,
repeat = 5)
print('Min Time: {}'.format(min(times)))
print('Max Time: {}'.format(max(times)))
排序算法--性能比较
在本节中,我们将进行三组测试。第一组将有100个随机数,第二组将有1000个,第三组将有10000个。好好看看这个表,比较一下时间的复杂性,并做出你自己的观察。我将在这之后分享我的观察结果。
| 排序算法 | 测试1 (100) | 测试2 (1000) | 测试3 (10000) |
|---|---|---|---|
| 泡沫排序 | 最小。0.01008秒最大 | ||
| :0.0206秒 | |||
| 最小。1.0242秒Max | |||
| : 1.0558秒 | |||
| 最小。100.922秒Max | |||
| : 102.475秒 | |||
| 插入排序 | 最小。0.00306秒Max | ||
| : 0.00650秒 | |||
| 最低限度。0.0369秒Max | |||
| : 0.0562秒 | |||
| 最小值。100.422秒Max | |||
| : 102.344秒 | |||
| 选择排序 | 最小。0.00556秒Max | ||
| : 0.00946秒 | |||
| 最低限度。0.4740秒Max | |||
| : 0.4842秒 | |||
| 最小值。40.831秒Max | |||
| : 41.218秒 | |||
| 快速排序 | 最小。0.00482秒Max | ||
| : 0.01141秒 | |||
| 最低限度。0.0370秒Max | |||
| : 0.0383秒 | |||
| 最低限度。0.401秒Max | |||
| : 0.420秒 | |||
| 合并排序 | 最小。0.00444秒Max | ||
| : 0.00460秒 | |||
| 最小。0.0561秒Max | |||
| : 0.0578秒 | |||
| 最小。0.707秒Max | |||
| : 0.726秒 | |||
| 堆排序 | 最小。0.00489秒Max | ||
| : 0.00510秒 | |||
| 最小。0.0704秒Max | |||
| : 0.0747秒 | |||
| 最小。0.928秒Max | |||
| : 0.949秒 | |||
| 计数排序 | 最小。0.01929秒Max | ||
| : 0.02052秒 | |||
| 最低限度。0.0354秒Max | |||
| : 0.0400秒 | |||
| 最小值。0.195秒Max | |||
| : 0.203秒 | |||
| 矩阵排序 | 最小。0.00315秒Max | ||
| : 0.00394秒 | |||
| 最小。0.0294秒Max | |||
| : 0.0309秒 | |||
| 最小。0.313秒Max | |||
| : 0.338秒 | |||
| 桶状分类 | 最小。0.00225秒Max | ||
| : 0.00241秒 | |||
| 最低限度。0.0335秒Max | |||
| : 0.0369秒 | |||
| 最小值。1.854秒Max | |||
| : 1.892秒 | |||
我还想包括对100,000和1,000,000个数字的测试,但O(n2)算法要花很长时间才能完成,所以我放弃了。
观察结果
- O(n2)算法(泡沫和插入排序)在测试数量增加到10,000时反应非常差。在10,000个数字时,其他算法的平均速度超过100倍。
- 在只有100个数字的测试案例中,O(n2)算法比O(n.log(n))算法快。
- 数字的数量每增加10倍,O(n2)算法的完成时间就增加100倍。
- 平均而言,Radix Sort和Counting Sort是最快的算法。
- Heapsort是最快的算法,空间复杂度为O(1)。
分类数据的比较
另一个非常有趣的情况是使用分类数据,而不是随机数据。这个测试主要是为了显示哪些排序算法在排序/部分排序的数据中表现良好,哪些表现较差。
| 排序算法 | 已排序的数据(1000)。 |
|---|---|
| 泡沫排序 | 最小。0.542秒最大值 |
| :0.556秒 | |
| 插入式排序 | 最小。0.790秒最大值 |
| :0.821秒 | |
| 选择排序 | 最小值。0.434秒Max |
| : 0.464秒 | |
| 快速排序 | 最小值。0.812秒最大值 |
| :0.872秒 | |
| 合并排序 | 最小。0.0289秒Max |
| : 0.0364秒 | |
| 堆积排序 | 最小。0.0604秒Max |
| : 0.0661秒 | |
| 计数排序 | 最小。0.0055秒Max |
| : 0.0124秒 | |
| 弧度排序 | 最小。0.0119秒Max |
| : 0.0145秒 | |
| 桶式排序 | 最小。0.0183秒Max |
| : 0.0247秒 | |
观察结果
- 惊喜吧,惊喜吧。快速排序算法名不副实,在上述所有算法中,对于1000个排序的数字,快速排序算法是最慢的。这是因为Quick Sort对这样的退化情况反应不佳,需要进行特殊的优化,如 "随机枢轴"。
- 除了快速排序之外,所有算法所需的时间都下降了。
- 计数排序的表现最好,其次是Radix和Bucket排序。