一、排序方法分类
1、按照操作方式不同可以分为:
1)插入排序:直接插入排序、希尔排序
2)交换排序:冒泡排序、快速排序
3)选择排序:直接选择排序、堆排序
4)归并排序:归并排序
5)分配排序:桶排序、基数排序
2、按照平均时间不同可以分为:
1)平方阶(O(n2))排序
一般称为简单排序,例如直接插入、直接选择和冒泡排序;
2)线性对数阶(O(nlgn))排序
如快速、堆和归并排序;
3)O(n1+£)阶排序
£是介于0和1之间的常数,即0<£<1,如希尔排序;
4)线性阶(O(n))排序
如桶、箱和基数排序。
3、按照是否涉及数据的内、外存交换分为:
1)内部排序
在排序过程中,若整个文件都是放在内存中处理,排序时不涉及数据的内、外存交换,则称之为内部排序(简称内排序)
2)外部排序
若排序过程中要进行数据的内、外存交换,则称之为外部排序。
外部排序指的是大文件的排序,面试的时候,面试官喜欢问,给你一个非常非常大的文件(比如1T),一行一个数(或者一个单词),内存最多只有8G,硬盘足够大,CPU很高级……然后要你给这个文件里面的数据排序。你要怎么办?
这其实就要用到外部排序。就是说要借助外存储器进行多次的内/外存数据的交换,因为内存不足以加载所有的数据,所以只能一部分一部分地加载。
二、排序算法的操作方法与存储方式
1、排序算法的基本操作
大多数排序算法都有两个基本操作:
- 比较两个关键字的大小。
- 改变指向记录的指针或移动记录本身。
注意:
第(2)种基本操作的实现依赖于待排序记录的存储方式。
2、待排文件的常用存储方式
1)以顺序表(或直接用向量)作为存储结构
排序过程:对记录本身进行物理重排,即通过关键字之间的比较判定,将记录移到合适的位置。
2)以链表作为存储结构
排序过程:无须移动记录,仅需修改指针。通常将这类排序称为链表(或链式)排序。
3)以顺序的方式存储待排序的记录,但同时建立一个辅助表(如包括关键字和指向记录位置的指针组成的索引表)
排序过程:只需对辅助表的表目进行物理重排(即只移动辅助表的表目,而不移动记录本身)。适用于难于在链表上实现,且仍需避免排序过程中移动记录的排序方法。
三、排序算法的性能分析
1、稳定性
在待排序的文件中,若存在多个关键字相同的记录,经过排序后这些具有相同关键字的记录之间的相对次序保持不变,该排序方法是稳定的;
若具有相同关键字的记录之间的相对次序发生变化,则称这种排序方法是不稳定的。
注意:
排序算法的稳定性是针对所有输入实例而言的。即在所有可能的输入实例中,只要有一个实例使得算法不满足稳定性要求,则该排序算法就是不稳定的。
2、影响排序效果的因素
因为不同的排序方法适应不同的应用环境和要求,所以选择合适的排序方法应综合考虑下列因素:
①待排序的记录数目n;
②记录的大小(规模);
③关键字的结构及其初始状态;
④对稳定性的要求;
⑤语言工具的条件;
⑥存储结构;
⑦时间和辅助空间复杂度等。
3、一些常见算法的性能比较
下面是一个总的表格,大致总结了我们常见的所有的排序算法的特点。
排序法 | 最好时间 | 平均时间 | 最差情形 | 稳定度 | 额外空间 | 备注 |
冒泡 | O(n) | O(n2) | O(n2) | 稳定 | O(1) | n小时较好 |
交换 | O(n2) | O(n2) | 不稳定 | O(1) | n小时较好 | |
简单选择 | O(n2) | O(n2) | O(n2) | 不稳定 | O(1) | n小时较好 |
直接插入 | O(n) | O(n2) | O(n2) | 稳定 | O(1) | 大部分已排序时较好 |
基数 | O(logRB) | O(logRB) | 稳定 | O(n) | B是真数(0-9), R是基数(个十百) | |
Shell | O(n) | O(nlogn) | O(ns) <s<2 | 不稳定 | O(1) | s是所选分组 |
快速 | O(nlogn) | O(nlogn) | O(n2) | 不稳定 | O(logn) | n大时较好 |
归并 | O(nlogn) | O(nlogn) | O(nlogn) | 稳定 | O(n) | n大时较好 |
堆 | O(nlogn) | O(nlogn) | O(nlogn) | 不稳定 | O(1) | n大时较好 |
四、排序算法的选择
1、不同条件下,排序方法的选择
(1)若n较小(如n≤50),可采用直接插入或直接选择排序。
当记录规模较小时,直接插入排序较好;否则因为直接选择移动的记录数少于直接插人,应选直接选择排序为宜。
(2)若文件初始状态基本有序(指正序),则应选用直接插人、冒泡或随机的快速排序为宜;
(3)若n较大,则应采用时间复杂度为O(nlgn)的排序方法:快速排序、堆排序或归并排序。
快速排序是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短;
堆排序所需的辅助空间少于快速排序,并且不会出现快速排序可能出现的最坏情况。这两种排序都是不稳定的。
若要求排序稳定,则可选用归并排序。但本章介绍的从单个记录起进行两两归并的 排序算法并不值得提倡,通常可以将它和直接插入排序结合在一起使用。先利用直接插入排序求得较长的有序子文件,然后再两两归并之。因为直接插入排序是稳定的,所以改进后的归并排序仍是稳定的。
2、针对各个排序算法的选择分析
1)快速排序(QuickSort)
从本质上来说,快速排序是归并排序的就地版本。
快速排序比大部分排序算法都要快。尽管我们可以在某些特殊的情况下写出比快速排序快的算法,但是就通常情况而言,没有比它更快的了。快速排序是递归的,对于内存非常有限的机器来说,它不是一个好的选择。
2)归并排序(MergeSort)
归并排序先分解要排序的序列,从1分成2,2分成4,依次分解,当分解到只有1个一组的时候,就可以排序这些分组,然后依次合并回原来的序列中,这样就可以排序所有数据。合并排序比堆排序稍微快一点,但是需要比堆排序多一倍的内存空间,因为它需要一个额外的数组。
3)堆排序(HeapSort)
堆排序适合于数据量非常大的场合(百万数据)。
堆排序不需要大量的递归或者多维的暂存数组。这对于数据量非常巨大的序列是合适的。比如超过数百万条记录,因为快速排序,归并排序都使用递归来设计算法,在数据量非常大的时候,可能会发生堆栈溢出错误。
4)Shell排序(ShellSort)
Shell排序比冒泡排序快5倍,比插入排序大致快2倍。Shell排序比起QuickSort,MergeSort,HeapSort慢很多。但是它相对比较简单,它适合于数据量在5000以下并且速度并不是特别重要的场合。它对于数据量较小的数列重复排序是非常好的。
5)插入排序(InsertSort)
插入排序通过把序列中的值插入一个已经排序好的序列中,直到该序列的结束。插入排序是对冒泡排序的改进。它比冒泡排序快2倍。一般不用在数据大于1000的场合下使用插入排序,或者重复排序超过200数据项的序列。
6)冒泡排序(BubbleSort)
冒泡排序是最慢的排序算法。在实际运用中它是效率最低的算法。它通过一趟又一趟地比较数组中的每一个元素,使较大的数据下沉,较小的数据上升。它是O(n^2)的算法。
7)交换排序(ExchangeSort)和选择排序(SelectSort)
这两种排序方法都是交换方法的排序算法,效率都是 O(n2)。在实际应用中处于和冒泡排序基本相同的地位。它们只是排序算法发展的初级阶段,在实际中使用较少。
8)基数排序(RadixSort)
基数排序和通常的排序算法并不走同样的路线。它是一种比较新颖的算法,但是它只能用于整数的排序,如果我们要把同样的办法运用到浮点数上,我们必须了解浮点数的存储格式,并通过特殊的方式将浮点数映射到整数上,然后再映射回去,这是非常麻烦的事情,因此,它的使用同样也不多。而且,最重要的是,这样算法也需要较多的存储空间。
参考:http://space.itpub.net/15203236/viewspace-616582
五、各个算法的链接
插入排序:http://www.cnblogs.com/aoguren/p/3289499.html
交换排序:http://www.cnblogs.com/aoguren/p/3287367.html
选择排序:http://www.cnblogs.com/aoguren/p/3292895.html