排序算法阶段总结(二)——如何选择合适的算法

当数据符合线性排序对数据的要求

当数据符合线性排序的要求时,优先使用线性排序。时间复杂度为O(n)。

但是对于大多数情况下,数据并不一定符合线性排序的数据要求。

小规模数据

如果对于小规模的数据排序,我们可以选择时间复杂度是 O(n²) 的算法。

因为在小规模数据面前,O(n²) 时间复杂度的算法并不一定比 O(nlogn) 的算法执行时间长。

我们通常进行的复杂度分析是偏理论的,大部分使用大O渐进复杂度表示。实际上时间复杂度并不等于代码实际的运行时间。

时间复杂度代表的是一个增长趋势,如果画成增长曲线图,会发现 O(n²) 比 O(nlogn) 要陡峭,也就是说增长趋势要更猛一些。

在大 O 复杂度表示法中,我们会省略低阶、系数和常数。

也就是说,O(nlogn) 在没有省略低阶、系数、常数之前可能是 O(knlogn + c),而且 k 和 c 有可能还是一个比较大的数。

假设 k=1000,c=200,当我们对小规模数据(比如 n=100)排序时,n2的值实际上比 knlogn+c 还要小。

knlogn+c = 1000 * 100 * log100 + 200 远大于10000

n^2 = 100*100 = 10000

 

大规模数据

如果对大规模数据进行排序,时间复杂度是 O(nlogn) 的算法更加高效。对于大规模数据,使用O(n²)的算法效率并不高。

所以,为了兼顾任意规模数据的排序,一般都会首选时间复杂度是 O(nlogn) 的排序算法来实现排序函数。

时间复杂度是 O(nlogn) 的排序算法有快速排序和归并排序,还有一个堆排序,快速排序和堆排序都有很多的应用。

但是,同为O(nlogn) 的排序算法的归并排序使用的并不多。那为什么呢?

快速排序当分区点选择极其不合理的时候,快速排序的最坏情况下时间复杂度为O(n²)。

而归并排序能做到最好、最坏平均情况下复杂度都为O(nlogn)。

主要原因是归并排序是一个非原地排序算法,空间复杂度为O(n)。

如果要排序 100MB 的数据,除了数据本身占用的内存之外,排序算法还要额外再占用 100MB 的内存空间,空间耗费就翻倍了。这就是归并排序相对于快速排序来讲最大的缺点。

快速排序的最坏情况下时间复杂度为O(n²),这种 O(n²) 时间复杂度出现的主要原因还是因为我们分区点选的不够合理。

我们可以通过优化快速排序来规避这种情况,来选择尽可能合理的分区点。如随机法、三/五/十数取中法

举例

C语言中的 qsort()。

从名字上看,很像是基于快速排序算法实现的,实际上它并不仅仅用了快排这一种算法。

在源码中,qsort() 会优先使用归并排序来排序输入数据。

因为归并排序的空间复杂度是 O(n),所以对于小数据量的排序,比如 1KB、2KB 等,归并排序额外需要 1KB、2KB 的内存空间,这个问题不大。现在计算机的内存都挺大的,我们很多时候追求的是速度。这就是一个典型的用空间换时间的应用。

如果数据量太大,排序 100MB 的数据,这个时候我们再用归并排序就不合适了。

所以,要排序的数据量比较大的时候,qsort() 会改为用快速排序算法来排序。

qsort() 选择分区点的方法就是“三数取中法”。

qsort() 并不仅仅用到了归并排序和快速排序,它还用到了插入排序。

在快速排序的过程中,当要排序的区间中,元素的个数小于等于 4 时,qsort() 就退化为插入排序,不再继续用递归来做快速排序

发布了91 篇原创文章 · 获赞 22 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_42006733/article/details/104476917
今日推荐