排序算法与代码实现

本文源自https://xuzhongcn.github.io/my/02/Sort.html

1、意义

排序算法在商业算法、信息搜索、运筹学、事件驱动模拟、数值计算、组合搜索等等领域都有着广泛的应用,不仅如此,定序序列往往是其他算法的前提条件,例如二分查找等。

2、概述

排序算法基本可以分为两大类:

3、言归正传

此文以最基本冒泡排序、选择排序作为开始;之后讨论插入排序、希尔排序、归并排序;在之后我们讨论快速排序分析JDK1.7以后的Arrays.sort中对于快速排序的优化,三向切分的快速排序与双轴快速排序。最后我们来看基于优先队列数据结构的堆排序,以及非基于比较的排序:计数排序、桶排序、基数排序。

4、算法理论与代码实现

4-1、冒泡排序
 
4-2、选择排序
 
4-3、插入排序
 
4-4、希尔排序
 
4-5、归并排序
 
4-6、基本快速排序

因为时间复杂度的问题,其实冒泡排序与选择排序在实际应用中并不多,因为它们是O(n^2)。快速排序时间复杂度要低,它是O(nlgn)。明显要快很多。

快速排序时间复杂度求解:

03

04

代码实现:

 
4-7、三向切分的快速排序

与基本的快速排序不同,我们改进快速排序,为了使得当“数组中有相同元素”时,不至于浪费比较次数。于是,我们向把数组分成三个部分,以数组中一个元素flag为标准,数组被拆分成{<flag,=flag,>flag},也就是小于,等于,大于三个部分。

在JDK1.7类java.util.DualPivotQuicksort.java中:01

我们可以看到,JDK中,以pivot为基本值,对数组进行比较,企图把数组拆分成三个部分,这里左边分界点为less,当k和great相遇之时,这一次快速排序拆分数组完成,也就是说less左边的都比pivot小,great右边的都比pivot大,于是下一次只需要:02

那么基于这种思想,我们可以写出三向切分的快速排序:

 
4-8、双轴快速排序

上面我们看到,快速排序一直都是以一个pivot值,来分裂数组的,双轴快速排序采用两个值,pivot1和pivot2,把数组拆分成三个部分{<pivot1,>=pivot1&&<=pivot2,>pivot2}。

在JDK1.7类java.util.DualPivotQuicksort.java中:

05

我们看到果真这般如此。less左边的都比pivot1小,great右边的都比pivot2大,当k与great相遇时,less与great之间的值就是介于[pivot1,pivot2]区间内部的值。

双轴快速排序代码实现:

 
4-9、堆排序

说到堆排序,它是基于优先队列的,优先队列概念大家自行补充,这里不再说明了,但是给出优先队列的代码实现:

 

有了优先队列,我们把元素放入优先队列里,然后一个一个取出来,就可以了。代码如下:

 
4-10、计数排序
 
4-11、桶排序
 
4-12、基数排序
 

5、总结

以上12种排序方式,基本就是目前流行的所有排序了,笔者在这里对于“自认为”比较难理解的“快速排序”、“三向切分的快速排序”、“双轴快速排序”,以及JDK1.7中的Arrays.sort进行了一些说明,谬误之处还望大家批评指正,不胜感激。