前言
排序算法一直是计算机核心算法之一,人们也在一直研究它们。现实情况中,直接依赖排序算法的事物有很多,间接依赖排序算法的事物有更多。
所以研究排序算法对于程序员来说很重要。
收获
排序算法不是相互孤立的,互相协作反而可以提高性能
例如快速排序,在分割(partition)之前,如果需要分割的区间过短(比如3),可以对该分割区间进行插入排序而不是继续快速排序。原因是对于小数组来说,快速排序要比插入排序慢(在数组规模较小的情况下,不能忽略渐进时间的常数项)。
在数据规模较小的情况下,不能忽略算法分析中忽略的常数项
考虑一种很极端的情况:只有两个数,分别用插入排序和快速排序排序。
从渐进时间来看,插入排序渐进时间为
n2
,快速排序渐进时间为
nlnn
。
而从实际程序来看,插入排序最多只需要一次比较和交换,而快速排序至少需要两次比较和一次交换,显然插入排序完胜快速排序。
快速排序并不是完美的排序算法
- 数组有序的时候,快速排序很慢,渐进时间为 nlnn
- 对于小数组来说,快速排序的比较和交换次数过多,速度变慢
其实没有任何算法可以堪称完美,在一方面出色,在另一方面就逊色。
既要考虑到内存访问,也要考虑到缓存访问
对于堆排序,时间复杂度 nlnn ,空间复杂度 n ,看似完美,实则不然。其余的算法存储空间为连续的,可以统一装入缓存,而堆排序是跳跃着访问元素,无法一次性将所需元素装入缓存。在实际应用中,堆排序还可能偏慢。
排序算法比较
算法 | 平均时间复杂度 | 空间复杂度 | 代码复杂度 | 稳定性 | 递归 | 适用规模 | 有序适应 |
---|---|---|---|---|---|---|---|
插入排序 | 低 | 否 | 否 | 小 | 好 | ||
归并排序 | 高 | 是 | 是 | 大 | 差 | ||
快速排序 | 高 | 否 | 是 | 大 | 差 | ||
堆排序 | 否 | 否 | 否 | 大 | 好 |
排序算法特殊用法
插入排序
希尔排序,将插入排序的步长(1)扩大,在慢慢减少至最小步长(1)。希尔排序很大程度上提高了插入排序的速度。希尔排序虽然很有用,但是具体提高速度的原因至今科学家都无法证明。
归并排序
对于小数组的归并,可以用插入排序。
快速排序
- 分割之前判断,若分割的区间很小,可以不去分割和排序,而是直接用插入排序排序
- 若数组重复元素较多,改进快速排序算法,变为三路划分快速排序
- 求第
k 个最大值的时候,可以根据快速排序的性质(每个分割过的坐标都是元素应该最终存在数组的位置),求解第 k <script type="math/tex" id="MathJax-Element-15">k</script> 个最大值。堆排序
可以用于优先队列(Priority Queue)。