11_如何选择一个合适的排序算法
作者:互联网
11_如何选择一个合适的排序算法
如何选择
-
线性排序算法的时间复杂度比较低,适用场景比较特殊。所以要写一个通用的排序函数,不能选择线性排序算法。
-
如果是小规模的数据,进行排序时可以选择时间复杂度是O(n^2)的算法;如果是大规模数据排序,一般选择时间复杂度是O(nlogn)的算法更加高效。
快速排序和归并排序如何选择
-
使用归并排序的情况不多,因为归并排序并不是原地排序算法,空间复杂度为O(n)。如果需要排序100MB的数据,除了数据本身占用的内存之外,排序算法还要额外再占用100MB的内存空间,空间资源耗费翻倍。
-
在实际使用中快排使用更多,快速排序在最坏的情况下时间复杂度为O(n^2),如何进行快排的优化。
优化快速排序
如果数据原来就是有序的或者接近有序的,每次分区点都选择最后一个数据,那快速排序算法就会变得非常糟糕,时间复杂度就会退化为O(n2)。实际上,这种O(n2)时间复杂度出现的主要原因还是因为我们分区点选得不够合理。
最理想的分区点是:被分区点分开的两个分区中,数据的数量差不多。
如果很粗暴地直接选择第一个或者最后一个数据作为分区点,不考虑数据的特点,肯定会出现之前讲的那样,在某些情况下,排序的最坏情况时间复杂度是O(n2)。为了提高排序算法的性能,我们也要尽可能地让每次分区都比较平均。
三数取中法
从区间的首、尾、中间,分别取出一个数,然后对比大小,取这3个数的中间值作为分区点。这样每间隔某个固定的长度,取数据出来比较,将中间值作为分区点的分区算法,肯定要比单纯取某一个数据更好。但是,如果要排序的数组比较大,那“三数取中”可能就不够了,可能要“五数取中”或者“十数取中”。
随机法
随机法就是每次从要排序的区间中,随机选择一个元素作为分区点。这种方法并不能保证每次分区点都选的比较好,但是从概率的角度来看,也不大可能会出现每次分区点都选得很差的情况,所以平均情况下,这样选的分区点是比较好的。时间复杂度退化为最糟糕的O(n2)的情况,出现的可能性不大。
举例分析排序函数
拿Glibc中的qsort()函数举例说明:
-
如果你去看源码,你就会发现,qsort()会优先使用归并排序来排序输入数据,因为归并排序的空间复杂度是O(n),所以对于小数据量的排序,比如1KB、2KB等,归并排序额外需要1KB、2KB的内存空间,这个问题不大。
-
但如果数据量太大,就跟我们前面提到的,排序100MB的数据,这个时候我们再用归并排序就不合适了。所以,要排序的数据量比较大的时候,qsort()会改为用快速排序算法来排序。
-
那qsort()是如何选择快速排序算法的分区点的呢?如果去看源码,你就会发现,qsort()选择分区点的方法就是“三数取中法”。
-
还有我们前面提到的递归太深会导致堆栈溢出的问题,qsort()是通过自己实现一个堆上的栈,手动模拟递归来解决的。
-
实际上,qsort()并不仅仅用到了归并排序和快速排序,它还用到了插入排序。在快速排序的过程中,当要排序的区间中,元素的个数小于等于4时,qsort()就退化为插入排序,不再继续用递归来做快速排序,因为我们前面也讲过,在小规模数据面前,O(n2)时间复杂度的算法并不一定比O(nlogn)的算法执行时间长。
即:一个排序的函数中并不一定是只用了一种排序算法,而是根据不同的情况,进行最优的选择。
标签:11,归并,分区,qsort,算法,排序,复杂度 来源: https://www.cnblogs.com/l12138h/p/16425510.html