排序优化:如何实现一个通用的、高性能的排序函数
几乎所有的编程语言都提供排序函数,这些排序函数是如何实现的?底层都利用了哪种排序算法?
如何选择合适的排序算法
对小规模数据进行排序,可以选择时间复杂度是o( n 2 n^2 n2)的算法,如果对大规模的数据进行排序,时间复杂度是O( n l o g n nlogn nlogn)更加高效
时间复杂度是O( n l o g n nlogn nlogn)的排序算法不止一个,有归并排序、快速排序、堆排序
归并排序并不是原地排序算法,空间复杂度是O( n 2 n^2 n2) 空间的耗费会翻倍
快排比较适合来实现排序函数
如何优化快速排序
什么最坏情况下快速排序的时间复杂度是O( n 2 n^2 n2),当数据原来本就是有序的或者接近有序的,每次分区点都选择最后一个数据,那快排算法就会变得很糟糕,时间复杂度退化成O( n 2 n^2 n2),主要原因还是因为我们分区点选的不够合理
最理想的分区点:被分区点分开的两个分区中,数据的数量差不多
如果不考虑数据的特点,肯定会出现最坏的情况,为了提高排序算法的性能,尽可能的让每次分区都比较平均
两个常用、简单的分区算法:
1.三数取中法
从区间的首、尾、中间分别取出一个数,然后对比大小,取这3个数的中间值作为分区点,这样每间隔某个固定的长度,取数据出来比较,将中间值作为分区点的分区算法,如果拍苏的数组比较大,可能需要“五数取中”“十数取中”
2.随机法
每次从要排序的区间中,随机选择一个元素作为分区点,这样时间复杂度退化成最糟糕的 n 2 n^2 n2,出现的可能性不大
快排是用递归来实现的,递归就要警惕堆栈溢出,为了避免快排中,递归过深而堆栈过小,导致堆栈溢出,有两种方法:一是限制递归深度,一旦递归过深,超过了我们的阈值就停止递归,第二种是通过在堆上模拟实现一个函数调用栈,手动模拟递归压栈、出栈的过程,就没有了系统栈大小的限制
举例分析排序函数
用Glibc中qsort()函数说明如何实现一个排序函数的
qsort()会优先使用归并排序来排序输入数据,因为归并排序的空间复杂度是o(n)所以对于小数据量的排序,比如1kb,2kb,归并排序额外需要1KB,2KB的内存空间,问题不大,用空间换时间,如果数据量太大,qsort()会改为用快排来排序
qsort()如何选择快排的分区点的? “三数取中法”
qsort()不仅仅用到了归并排序和快排,也用到了插入排序,在快速排序的过程中,当要排序的区间中,元素的个数小于等于4,就退化为插入排序。在小数据量中,o( n 2 n^2 n2)时间复杂度的算法并不一定比O( n l o g n nlogn nlogn)的算法执行时间长
时间复杂度代表的是一个增长趋势,小规模的数据排序中,o( n 2 n^2 n2)的排序算法并不一定比o( n l o g n nlogn nlogn)的执行时间长,对于小规模的排序,选择不需要递归的简单的插入排序算法