11种常用排序算法速览

最近学了下排序算法,也乱七八糟的实现了一下,这里就将我所掌握的11种常见的排序算法总结如下(文章中我统一按由小到大的顺序来描述)
先了了解下算法的两个基本特性
1.我们通常所关注的大O标记是指其时间复杂度,但一个算法的性能不仅仅是由这一个指标所决定的,还有空间复杂度。这个里就涉及到一个简略的概念:原地排序 。符合原地排序的算法在执行过程中不会要求有额外的内存分配,典型的快速排序就是这样,而归并排序则不是原地排序。
2.还有一个概念是稳定排序 ,指的是相同的值在经过排序之后的顺序还是不变。如1,2,1,2,1,3在排序之前3个1的顺序是就如其字体的颜色(这个颜色只是为了便于查看哈),在经过稳定排序之后,其为1,1,1,2,2,3;而不稳定排序则可能是1,1,1,2,2,3 相同数值的元素的顺序就被打乱了。在给定一输入顺序时,稳定排序是具有可预测性的,而不稳定排序则无法准确的预测。这个特性在某些场合还是十分有必要的,比如在处理优先队列时。举例说下吧,在处理成绩表时,先按语文的高低排序,再按数学的成绩排序,若是稳定排序,则数学成绩相等的同学,排在前面的语文成绩一定比后面的同学好;相反,若是不稳定排序则不具备这种特性。
常见的排序算法大概有11种。按排序类型来分的话则可分为3类。我这里的分类与常规的分类方法有所不同,并不是按照其插入方式来进行的,而是按照其执行的思想来分类,我觉得这样比较容易理解吧..
一.冒泡排序,插入排序,选择排序
二.堆排序,中值排序,快速排序,归并排序,希尔排序
三.计数排序,基数排序,桶排序

第一类是基于两两比较 的,这类算法用在小规模的数据且初始数据近乎已排好序,在这种情况下,其性能优势很明显,不会比快速排序等“高阶”算法差多少:
1.冒泡排序: 原地+稳定 这个算法的思想很简单,也很容易实现。操作步骤就是两两比较使得较小元素往上浮动。
2.插入排序: 原地+稳定 其思想是从头部到尾,逐渐的确定每一个元素的位置,再执行数据移动。
3.选择排序: 原地+不稳定 其思想是从头部到尾,逐渐的确定每一个位置的元素,在执行交换。我个人比较喜欢选择排序一点,虽然插入排序的时间复杂度在最佳情况下是线性的,但其数据移动的规模太大了。在对操作系统及编译器优化的进一步深入了解之前,我还是觉得大规模移动数据的开销要更厉害一些。
PS:选择排序和插入排序是一个相反的过程,插入排序是确定一个元素的位置,而选择排序是确定这个位置的元素。只是插入排序的时间复杂度在最好情况下为O(n)而选择排序为O(n*n),当然空间复杂度则可就反过来了,具体那个好一些得看具体的体系结构吧。

第二类则是虽然也是基于两两比较,但统一的采用了分治的思想 ,所以在时间复杂度上就不再限于O(n*n)了。
1.堆排序: 原地+不稳定 比较适用于基于数组的排序,其策略是先建立一个最大二叉堆,然后将数组头部的元素与堆尾互换,使最大的元素处在最后,然后更新维护使之继续保持为最大二叉堆。如此循环,最终就可以获得一个有序序列
2.快速排序: 原地+不稳定 最为著名的算法当属快速排序了,容易实现性能高效
3.中值排序: 原地+不稳定 中值排序的方法是先遍历数组以获取中值(这个中值是严格的中间值,如一个序列23,18,17,14,82,56,31.序列中31为中值,那么会将31置换到序列正中间),然后十分平均的二分再递归,至于怎么个搜索到中值则会有不同的算法。这个算法一般书上讲得相对来说较少,而且也用得少,因为其跟快速排序的思想几乎一样,但因为其严格寻找出中值,所以能够较好的处理最差情况,当然优化的快速排序也不会差到哪去。
4.归并排序: 非原地+稳定 归并排序也算是小有名气吧,其思想就是分治,将源数据一分为二分别进行排序然后再在线性时间内完成合并。由于要申请额外的空间,所以其应用并不如快速排序那样广泛,但还是应该好好掌握其精髓。
5.希尔排序: 原地+非稳定 其具体的操作步骤按步长将数组分为几个小数组,然后采用起他的排序算法将各小数组进行排序即可。希尔排序省去了相邻两元素之间的直接比较,所以速度上的优势是极为明显的。

第三类则不是基于两两比较了,这三个算法的策略基本相同,都是采用额外的数据结构来辅助进行排序 。由于三者中比较著名的是桶排序,加上三者的思想的确比较类似,所以容易混淆,一度以为基数排序就是桶排序的一种应用。不过只要算法的思想精髓掌握了,名字什么的都只是浮云。
1.计数排序: 非原地+稳定 计数排序先遍历源数据以获取最大和最小的数据,然后确定创建一个多大的数组(如一堆含有1000个整数的数据,分布在[10,50]中,那么可以创建一个具有41个元素数组);再遍历一遍源数据,统计出源数据中不同的元素出现的次数;最后根据这个数组即可快速的进行排序。使用这个算法的前提条件是输入数据具有一确定的较小范围,若范围过大则在空间上会造成过大的损失。
2.基数排序: 非原地+稳定 基数排序常用在对数字数据的排序,根据不同数位的值来进行多次排序,跟桶排序有异曲同工之妙。不过得注意的是权值低的位先排,权值高的则后排。
3.桶排序: 非原地+稳定 桶排序的思想乃至操作步骤几乎与桶排序完全相同,只是应用的场景略有不同。桶排序可以通过使用hash函数来完成桶的选择,如果将数字的位值也当作hash的话,则的确有点可以将计数排序理解成桶排序。

 

具体的算法简略描述已经完了,其实简略的描述也不是很好的能够能够描述算法的思想,我这里只是做了一个简单的概括,能够从全貌的直接了解算法采取的策略。就如标题所言,这篇文章只是对11中常用的排序算法进行一个十分简略的表述,所以需要进行深入研究的可能会在这里无法获得较大收益....这里有两篇文章,用Java实现了常见的几种算法,注释相当详细,可做参考吧,我自己的代码就不出来献丑了。
常用排序算法分析与实现(一)(Java版)
常用排序算法分析与实现(二)(Java版)

 

最后用表格来系统的总结一下各个排序算法的一些特性吧(同时附上维基百科中相应算法的链接,上面有详细的代码演示):
算法名称 原地排序 稳定排序 时间复杂度(最佳/最差/平均) 适用的情景
冒泡排序 yes yes O(n*n)/O(n*n)/O(n*n) 仅对于少量数据的排序才会适用,但相对来说并没有什么很大的优势
插入排序 yes yes O(n)/O(n*n)/O(n*n) 在处理小数据尤其是数据基本上已排好序的前提下,则插入排序当属首选了。不过仍然逃不过只能适用于小量数据的排序,数量一般不超过1000
选择排序 yes no O(n*n)/O(n*n)/O(n*n) 交换的次数比冒泡排序来说要少一点,而且也较好实现,不过仍然仅限于少量数据的处理
堆排序 yes no O(nlogn)/O(nlogn)/Θ(nlogn) 堆排序不是一个稳定的排序,而且非常频繁的移动数据,建议不要使用在基于值的数据,但可以用于指针类型排序。
中值排序 yes no Θ(nlogn)/Θ(n2)/Θ(nlogn) 和快速排序基本相同,但快速排序应用得更广泛一点。
快速排序 yes no Θ(nlogn)/Θ(n2)/Θ(nlogn) 应用相当的广泛,必须熟练掌握的算法之一。但因为不是一个稳定排序,所以在要求具有稳定性的情况下是不能够适用的。
归并排序 no yes Θ(n)/Θ(nlogn)/Θ(nlogn) 在时间复杂度上相对快速排序来说并没有什么太大优势,空间复杂度却有较高,所以在快速排序面前基本上没有什么太大优势,但也是最好熟练掌握的一种算法
希尔排序 yes no O(n)/O(n)/根据步长来具体确定 比较偏门的算法吧,应用之处倒也没有什么特别要求,全凭环境而定吧
计数排序 no yes O(n)/O(n)/O(n) 在数据范围很小时,这个就基本上是最实用高效的算法了吧
基数排序 no yes O(n)/O(n)/O(n) 和桶排序的思想类似,但基本上是单一的应用在数值排序上
桶排序 no yes O(n)/O(n)/O(n) 当数据能够以一个快速hash函数均匀的分配时,桶排序就是最快的排序算法了

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章