Java双基准快速排序算法java.util.DualPivotQuicksort源码阅读

Java双基准快速排序算法源码阅读

jdk版本:jdk1.8.0_191

Java数组和List对象的排序方法分别是Arrays.sort(T[] a)和Collections.sort(List<T> list),分别位于java.util.Arrays和java.util.Collections类中. 由于List底层采用数组实现,因此Collections.sort方法也是通过调用Arrays.sort方法实现的。

Arrays.sort方法源码很简单,就是调用了DualPivotQuicksort.sort方法。

/**
* Sorts the specified array into ascending numerical order.
*
* <p>Implementation note: The sorting algorithm is a Dual-Pivot Quicksort
* by Vladimir Yaroslavskiy, Jon Bentley, and Joshua Bloch. This algorithm
* offers O(n log(n)) performance on many data sets that cause other
* quicksorts to degrade to quadratic performance, and is typically
* faster than traditional (one-pivot) Quicksort implementations.
*
* @param a the array to be sorted
*/
public static void sort(int[] a) {
   
    DualPivotQuicksort.sort(a, 0, a.length - 1, null, 0, 0);
}

简单来说调用关系就是:

DualPivotQuicksort.sort -----> Arrays.sort -----> Collections.sort

于是再跟进去看DualPivotQuicksort.sort方法,位于java.util.DualPivotQuicksort类。从java7开始,针对排序方法的底层算法做了改进,从快速排序(Quicksort)改进为双基准快速排序(DualPivotQuicksort). DualPivotQuicksort,顾名思义,就是用2个pivot把一个待排序的数组分成3段,对这3段分别进行递归调用。关于DualPivotQuicksort相对于Quicksort能够提升速度的原因,这篇博文给出了很详尽的解释:新的快速排序算法: 《Dual-Pivot QuickSort》阅读笔记.

以int型数组为例,DualPivotQuickSort类中直接被Arrays.sort调用的方法的函数签名是

/**
* Sorts the specified range of the array using the given
* workspace array slice if possible for merging
*
* @param a the array to be sorted
* @param left the index of the first element, inclusive, to be sorted
* @param right the index of the last element, inclusive, to be sorted
* @param work a workspace array (slice)
* @param workBase origin of usable space in work array
* @param workLen usable size of work array
*/
static void sort(int[] a, int left, int right,
                 int[] work, int workBase, int workLen)

该default权限方法调用的是另一个同名的private方法sort(int[] a, int left, int right, boolean leftmost),它是双基准快速排序的算法实现,其方法注释如下:

/**
* Sorts the specified range of the array by Dual-Pivot Quicksort.
*
* @param a the array to be sorted
* @param left the index of the first element, inclusive, to be sorted
* @param right the index of the last element, inclusive, to be sorted
* @param leftmost indicates if this part is the leftmost in the range
*/
private static void sort(int[] a, int left, int right, boolean leftmost)

首先,属性INSERTION_SORT_THRESHOLD规定了使用插入排序/递归式快速排序的阈值,在这里阈值取为47. 插入排序是O(n2)的算法,而快速排序是平均复杂度O(nlogn)、最坏复杂度O(n2)的算法,但是由于快速排序的实现使用了递归,有函数调用的开销,因此在n特别小的时候反而不如复杂度为O(n^2)的非递归算法。

待排序的数组长度小于47时,就会使用插入排序。根据leftmost参数(待排序的分段是否是数组最左边的分段),对插入排序进行了优化,使用了所谓的pair insertion sort,具体可以看代码。

待排序的数组长度大于47时,就会使用递归式的双基准快速排序。双基准排序的思想如下图所示:

/*
 * Partitioning:
 *
 *   left part           center part                   right part
 * +--------------------------------------------------------------+
 * |  < pivot1  |  pivot1 <= && <= pivot2  |    ?    |  > pivot2  |
 * +--------------------------------------------------------------+
 *               ^                          ^       ^
 *               |                          |       |
 *              less                        k     great
 *
 * Invariants:
 *
 *              all in (left, less)   < pivot1
 *    pivot1 <= all in [less, k)     <= pivot2
 *              all in (great, right) > pivot2
 *
 * Pointer k is the first index of ?-part.
 */

首先从选取5个备选基准:e1, e2, e3, e4, e5,分别近似位于数组待排序部分的3/14, 5/14, 7/14, 9/14, 11/14, 用插入排序将e1, e2, e3, e4, e5升序排列,选取5/14位置和9/14位置的两个备选基准作为两个基准: pivot1, pivot2。通过3个指针less, great和k,将数组待排序部分分成3段:最左边< pivot1的一段、中间>= pivot1 && <= pivot2的一段、最右边 > pivot2的一段,然后递归调用3次分别将这3段数组排序。

最后附上算法实现的完整源码。

附录:java.util.DualPivotQuicksort.sort(int[] a, int left, int right, boolean leftmost)方法完整源码

/**
 * Sorts the specified range of the array by Dual-Pivot Quicksort.
 *
 * @param a the array to be sorted
 * @param left the index of the first element, inclusive, to be sorted
 * @param right the index of the last element, inclusive, to be sorted
 * @param leftmost indicates if this part is the leftmost in the range
 */
private static void sort(int[] a, int left, int right, boolean leftmost) {
   
	int length = right - left + 1;

	// Use insertion sort on tiny arrays
	if (length < INSERTION_SORT_THRESHOLD) {
   
		if (leftmost) {
   
			/*
			 * Traditional (without sentinel) insertion sort,
			 * optimized for server VM, is used in case of
			 * the leftmost part.
			 */
			for (int i = left, j = i; i < right; j = ++i) {
   
				int ai = a[i + 1];
				while (ai < a[j]) {
   
					a[j + 1] = a[j];
					if (j-- == left) {
   
						break;
					}
				}
				a[j + 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值