前面几节讲了几种排序算法(包括算法思想、算法实现、算法分析),本节将要对这几种算法进行一个综合比较分析,大致有如下结果:
排序算法 | 平均时间 | 最坏情况 | 稳定性 | 辅助存储 |
直接插入排序 | O(n2) | O(n2) | 稳定 | O(1) |
Shell排序 | O(nlogn) | O(ns) 1<s<2 | 不稳定 | O(1) |
冒泡排序 | O(n2) | O(n2) | 稳定 | O(1) |
快速排序 | O(nlogn | O(n2) | 不稳定 | O(nlogn) |
简单选择排序 | O(n2) | O(n2) | 不稳定 | O(1) |
堆排序 | O(nlogn) | O(nlogn) | 不稳定 | O(1) |
2-路归并排序 | O(nlogn) | O(nlogn) | 稳定 | O(n) |
(1) 从平均时间性能上看,快速排序最佳,其所需时间最省,但其最坏情况下的时间性能不如堆排序和归并排序。后两者的比较结果是,在n较大时,归并排序所需时间较堆排序省,但所需辅助空间多。
(2) 上述直接插入排序、冒泡排序、简单选择排序一般称为简单排序。其中直接插入排序最简单,但序列中的记录基本有序或n值很小时时,它是最佳的排序方法,因此常将它和其它的排序算法如快速排序、归并排序等结合在一起使用。
(3) 从方法性能上来看,基数排序是稳定的内部算法,所有时间复杂度为O(n2)的简单排序都是稳定的,所,然而快速排序、堆排序、希尔排序等时间性能较好的排序算法都是不稳定的。
(4) 上述各种算法中没有哪一种算法是绝对最好的。有的适用于n值较小的,有的适用于n值较大的,每一种算法都有其适用的情况。
不同条件下,排序方法的选择
(1)若n较小(如n≤50),可采用直接插入或直接选择排序。
当记录规模较小时,直接插入排序较好;否则因为直接选择移动的记录数少于直接插人,应选直接选择排序为宜。
(2)若文件初始状态基本有序(指正序),则应选用直接插人、冒泡或随机的快速排序为宜;
(3)若n较大,则应采用时间复杂度为O(nlgn)的排序方法:快速排序、堆排序或归并排序。
快速排序是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短;
堆排序所需的辅助空间少于快速排序,并且不会出现快速排序可能出现的最坏情况。这两种排序都是不稳定的。
若要求排序稳定,则可选用归并排序。上一节介绍的从单个记录起进行两两归并的 排序算法并不值得提倡,通常可以将它和直接插入排序结合在一起使用。先利用直接插入排序求得较长的有序子文件,然后再两两归并之。因为直接插入排序是稳定的,所以改进后的归并排序仍是稳定的。
(4)在基于比较的排序方法中,每次比较两个关键字的大小之后,仅仅出现两种可能的转移,因此可以用一棵二叉树来描述比较判定过程。
(5)前面几节给出的排序算法,输人数据均是存储在一个向量中。当记录的规模较大时,为避免耗费大量的时间去移动记录,可以用链表作为存储结构。譬如插入排序、归并排序、基数排序都易于在链表上实现,使之减少记录的移动次数。但有的排序方法,如快速排序和堆排序,在链表上却难于实现,在这种情况下,可以提取关键字建立索引表,然后对索引表进行排序。然而更为简单的方法是:引人一个整型向量t作为辅助表,排序前令t[i]=i(0≤i<n),若排序算法中要求交换R[i]和R[j],则只需交换t[i]和t[j]即可;排序结束后,向量t就指示了记录之间的顺序关系:
R[t[0]].key≤R[t[1]].key≤…≤R[t[n-1]].key
若要求最终结果是:
R[0].key≤R[1].key≤…≤R[n-1].key
则可以在排序结束后,再按辅助表所规定的次序重排各记录,完成这种重排的时间是O(n)。