转自:点击打开链接 并结合部分个人修改
1. 简述
本文主要说明一些常用的内部排序算法的分类、复杂性和稳定性。主要基于现在的理解和学习,详细准确的复杂度可以参见维基百科等比较权威的网站,对于一些算法的不同实现,复杂度也不同,这里给出的复杂度都是相对较好的算法的复杂度。
3. 复杂性和稳定性
空间复杂度定义
空间复杂度(Space Complexity)是对一个算法在运行过程中临时占用存储空间大小的量度(考虑的是全局辅助空间,对于递归而言,每一次生成的局部空间用完就释放了,因此不计算在内),记做S(n)=O(f(n))。比如直接插入排序的时间复杂度是O(n^2),空间复杂度是O(1) 。而一般的递归算法就要有O(n)的空间复杂度了,因为每次递归都要存储返回信息,但特别注意也可以通过一些办法使得递归不返回数据,从而降低空间复杂度,例如空间复杂度为O(1)的归并排序:点击打开链接。一个算法的优劣主要从算法的执行时间和所需要占用的存储空间两个方面衡量。
稳定性定义:
假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,ri=rj,且ri在rj之前,而在排序后的序列中,ri仍在rj之前,则称这种排序算法是稳定的;否则称为不稳定的。
各种常用排序算法 |
||||||
类别 |
排序方法 |
时间复杂度 |
空间复杂度 |
稳定性 |
||
平均情况 |
最好情况 |
最坏情况 |
||||
插入排序 |
直接插入 |
O(n2) |
O(n) |
O(n2) |
O(1) |
稳定 |
shell排序 |
O(n1.3) |
O(n) |
O(n2) |
O(1) |
不稳定 |
|
选择排序 |
直接选择 |
O(n2) |
O(n2) |
O(n2) |
O(1) |
不稳定 |
堆排序 |
O(nlog2n) |
O(nlog2n) |
O(nlog2n) |
O(1) |
不稳定 |
|
交换排序 |
冒泡排序 |
O(n2) |
O(n2) |
O(n2) |
O(1) |
稳定 |
快速排序 |
O(nlog2n) |
O(nlog2n) |
O(n2) |
O(log2n) |
不稳定 |
|
归并排序 |
O(nlog2n) |
O(nlog2n) |
O(nlog2n) |
O(n) |
稳定 |
|
基数排序 |
O(d(r+n)) |
O(d(n+rd)) |
O(d(r+n)) |
O(rd+n) |
稳定 |
|
注:基数排序的复杂度中,r代表数据个数,d代表关键字维度,n代表箱子的个数 |
时间复杂度和部分空间复杂度分析
冒泡排序:在已经有序的情况,取得O(N)的复杂度。
快速排序:每次递归都是N的复杂度,递归次数根据序列有关系,当已经有序的情况下,递归N次,时间复杂度为O(N*LogN)。就空间复杂度来说,主要是递归造成的栈空间的使用,最好情况,递归树的深度为log2n,其空间复杂度也就为O(logn),最坏情况,需要进行n‐1递归调用,其空间复杂度为O(n),平均情况,空间复杂度也为O(logn)。对于快排的优化,可以参考:点击打开链接。
插入排序:在已经有序的情况,取得O(N)的复杂度。
希尔排序:最差时间和平均时间都要根据步长数组来判断,现在最好算法的最差时间复杂度,为O(N*LogN)。空间主要是步长数组需要的。一般步长之间不要存在公因子。
插入排序:交换次数比冒泡排序少多了,由于交换所需CPU时间比比较所需的CPU时间多,n值较小时,选择排序比冒泡排序快。
堆排序: 堆排序属于选择排序,选择N-1次,每次从堆中选择一个,因此最差就是N*LogN。最好的时间无法准确估计,基本上也是N*LogN这个程度。空间复杂度,堆排序(以最小堆为例)每次将最小元素上升到堆顶,获取之,然后删除改元素,把堆中最后一个元素放到堆顶,重复上述操作。由于在排序中只存在1个元素的交换,所以空间复杂度为O(1)
注意:快排和堆排空间复杂度的区分。虽然都有递归调用,但是快排每次递归是需要返回值的,因此空间复杂度就是栈的深度O(logn)而堆排则是就地调整,每次就是交换一下栈顶元素,调整好就调整下一个,并不存在值的返回,因此复杂度为O(1)
归并排序:这个排序算法的复杂度与数组初始化序列无关,归并次数为LogN次,每次复杂度是O(N),因此复杂度都是O(N*LogN)。就空间复杂度考虑,算法处理过程中,需要一个大小为n的临时存储空间用以保存合并序列。若想要令空间复杂度为O(1),则需要在Merge时利用已有的两个数组,用插入排序将数组B插入到数组A,但这样的代价是又多一层循环(插入排序),因此时间复杂度变为了O(n^2 logn).详见:点击打开链接。
基数排序:K是数组中,数值的最大位数。对于int来说,使用十进制的话,K<=10。稳定性来说,基数排序要求每次必须使用稳定排序,否则最终得不到正确结果。所以这里的稳定是必须的,而不可能存在不稳定的。而其他的排序,比如冒泡排序,可以改写成不稳定的,这点需要注意。
计数排序:K是数组中,数值的范围,即K=Max-Min+1。对于int来说,最坏的情况为K=2^32,即数组内包含了最大正数和最小负数。
桶排序:由于桶排序可能有的桶没有数据,那么假设N个数据,只被分到了M个桶中,O(N)+O(M*(N/M)*log(N/M))=O(N+N*(logN-logM))=O(N+N*logN-N*logM) =O(N+N*(logN-logM)),当分到的桶越多时,越是接近N。计数排序可以看作桶排序的一个特例,将桶开的很大,直到能够保证每个不同的数值都被分到一个桶中。
稳定性分析:
4. 参考
维基百科
三种线性排序算法 计数排序、桶排序与基数排序 http://www.byvoid.com/blog/sort-radix/
桶排序与基排序 http://anwj336.blog.163.com/blog/static/8941520920109535025216/
百度百科