我这里只总结各大算法知识的要点,如果你想看看算法思想和实现代码,网上的其他博客都很喜欢贴大段代码和文本,可以自己去看。
(如果出错,请指正!感激不尽!)
一.三大简单、慢速排序算法
|
平均 |
最好 |
最坏 |
辅助存储 |
稳定性 |
直接插入 |
n^2 |
n |
n^2 |
1 |
稳定 |
直接选择 |
n^2 |
n^2 |
n^2 |
1 |
不稳定 |
直接交换(冒泡) |
n^2 |
n |
n^2 |
1 |
稳定 |
我省略了O,如:上面的n^2其实是O(n^2)的意思
这三种算法的共同优点都是简单,共同缺点都是慢。
|
优点 |
缺点 |
直接插入 |
|
比较次数越少,移动次数越多 |
直接选择 |
|
|
直接交换(冒泡) |
|
每次只移动相邻两个元素 |
二.希尔排序
n^1.3 n n^2 1 不稳定
希尔排序中,算法的效率很大程度由增量决定,而一个合适的增量的选择需要大量的经验。
三.堆排序
nlog2n nlog2n nlog2n 1 不稳定
nlog2n的2是下标,在这里不知道怎么设置,大家应该了解。
它比快速排序的优点:在最坏情况下它的性能很优越
它比归并排序的有点:使用的辅助存储少
它的缺点: 不适合太小的待排序列(因为他需要建堆);不稳定,不适合对象的排序
四.快速排序、归并排序(最常用的排序算法)
|
平均 |
最好 |
最坏 |
辅助存储 |
稳定性 |
快速排序 |
nlog2n |
nlog2n |
n^2 |
1 |
不稳定 |
归并排序 |
nlog2n |
nlog2n |
nlog2n |
n |
稳定 |
(分治法思想)
PS:二者各自的特点。
快速排序最快的排序算法,缺点是不稳定,不适合对象排序;
归并排序第二块的算法,缺点是辅存很大,适合对象排序;
PS:快速排序递归堆栈空间的占用:
最优的情况下空间复杂度为:O(logn) ;每一次都平分数组的情况
最差的情况下空间复杂度为:O(n );退化为冒泡排序的情况
PS:一个排整数的测试:
在笔记本上跑的,对1024*1024个随机整数排序,快速排序比合并排序快3到4倍。
[05.0813:40:22] INFO: Performance Check: Quick Sort 1048576 Elements., took 94 ms
[05.0813:40:22] INFO: Performance Check: Merge Sort 1048576 Elements., took 437 ms
五.以Java 集合类的sort()排序为例子(jdk不同版本sort()实现也不同)
1.JDK6中的排序是基于传统的归并排序做了部分优化,这两个优化都很简单,实际上效率并未提高多少。所以在JDK7中将其替换为TimSort。
2.JDK 7中,内部实现换成了TimSort,其对对象间比较的实现要求更加严格,
(1)传入的待排序数组若小于阈值MIN_MERGE(Java实现中为32,Python实现中为64),则调用binarySort,这是一个不包含合并操作的 mini-TimSort。二分查找/折半插入查找,
从数组开始处找到一组连接升序或严格降序(找到后翻转)的数。
(2)否则, 不断二分,直到小于阈值,然后插入排序;
TimSort算法是一种起源于归并排序和插入排序的混合排序算法
3.在jdk8中,如果你看过源码就会知道,其实针对不同的情况使用了不同的排序算法,简单罗列下:
(1).如果是简单对象数据,例如int,double,且数组长度在一定阀值内,则使用快排,如果在阀值外,则用归并的变种;
(2).如果是复杂对象数组,则如果数组长度在一定阀值以内,则使用折半插入排序,如果长度在阀值外,则使用归并法,但是如果归并二分后小于阀值了,则在内部还是会使用折半插入排序。。。以上只是大概
大家一定注意到了,阈值以外需要使用归并,因为快排使用递归,如果待排序列太大的话,堆栈可能会溢出;而归并排序不存在此问题;
我查阅了jdk8的新功能,其中好像并没有关于sort()的更新,这样说来,jdk7和8应该在sort()上没有区别才对;但是我在知乎上找到了关于jdk8 的sort()描述,好像还有很多人同意的样子,所以我就把他的言论给贴了过来。
后来这一块还是没弄明白,发了个求助的帖子,有了结果后,我会修改这个博文的。若是有高手看到此处,还望不吝赐教啊!