java源码 Array.sort()
在学习Array.sort()的排序规则之前应该先了解以下几种排序。
- 插入排序及其优化算法
- 快速排序及其双轴快速排序算法
- Tim排序
- 归并排序
源码中,就是这几种排序混合使用。
下文以整形数组为例进行解读。
对传入Array.sort()中的数组,会传入DualPivotQuicksort.sort()方法中实现排序。
//a是数组,0是左边界,a.length-1是右边界,
//后三个参数是关于操作空间的,其存在的实际用途我也不太明白,
//若有大佬晓得,还请指点迷津。
public static void sort(int[] a) {
DualPivotQuicksort.sort(a, 0, a.length - 1, null, 0, 0);
}
对源码的代码我做了略微的修改,方便阅读,与源码有略微差异
初次判断排序方式
判断数组长度是否达到286,若 length < 286,使用快速排序,否则进行Tim排序检验
//如果排序数组长度小于286,进行快速排序,sort的参数true是表示使用插入排序,false是使用优化版的插入排序。
if (right - left < QUICKSORT_THRESHOLD) {
sort(arr, left, right, true);
return arr;
}
插入排序
在快速排序中若被排序数组段长度小于47,使用插入排序。根据参数leftmost是否是true决定用普通插入排序还是优化后的插入排序
//插入排序
if (length < INSERTION_SORT_THRESHOLD) {
//插入排序
if (leftmost) {
for (int i = left, j = i; i < right; j = ++i) {
int ai = arr[i + 1];
while (ai < arr[j]) {
arr[j + 1] = arr[j];
if (j-- == left) {
break;
}
}
arr[j + 1] = ai;
}
}
//优化的插入排序
else {
//跳过升序阶段
do {
if (left >= right) {
return;
}
} while (arr[++left] >= arr[left - 1]);
/**
* 插入排序的优化算法
* 一次性进行两个数值的排序,
* 将两个数值进行比较,选出最大的值作为标志数和左边的数值依次进行对比,被比较的数若大于标志数,该数向右移动两个位置,
* 直到该数小于标志数,将标志数放在小于标志数的数的右边第二位,将另一个数作为标志数向左比较,被比较的数若大于标志数,该数向右移动一个位置,
* 直到该数小于标志数,将标志数放在小于标志数的数的右边第一位。
* */
for (int i = left; ++left <= right; i = ++left) {
int a1 = arr[i], a2 = arr[left];
if (a1 < a2) {
a2 = a1;
a1 = arr[left];
}
while (a1 < arr[--i]) {
arr[i + 2] = arr[i];
}
arr[++i + 1] = a1;
while (a2 < arr[--i]) {
arr[i + 1] = arr[i];
}
arr[i + 1] = a2;
}
//上述排序是成对排序,若数组数是单数,则需要对数组最后一位数排序
int last = arr[right];
while (last < arr[--right]) {
arr[right + 1] = arr[right];
}
arr[right + 1] = last;
}
return;
}
快速排序
对于java中的快速排序,数组长度要超过47,然后设置五个点位,分别为e1,e2,e3,e4,e5。并且 a[e1]<=a[e2]<=a[e3]<=a[e4]<=a[e5];
若是五个点位上的数不相等,即 a[e1] != a[e2] && a[e2] != a[e3] && a[e3] != a[e4] && a[e4] != a[e5] ,则使用双轴快速排序。以e2为左轴,e4为右轴。
若是五个点位上有数相等,则使用单轴快速排序。即以e3为轴。
//快速排序
//数组长度除以7 ,即 length / 7;
int seventh = (length >> 3) + (length >> 6) + 1;
int e3 = (right + left) >>> 1;
int e2 = e3 - seventh;
int e4 = e3 + seventh;
int e1 = e2 - seventh;
int e5 = e4 + seventh;
//保证 arr[e1] <= arr[e2] <= arr[e3] <= arr[e4] <= arr[e5]
if (arr[e2] < arr[e1]) {
int t = arr[e2]; arr[e2] = arr[e1]; arr[e1] = t;
}
if (arr[e3] < arr[e2]) {
int t = arr[e3]; arr[e3] = arr[e2]; arr[e2] = t;
if (t < arr[e1]) {
arr[e2] = arr[e1]; arr[e1] = t;
}
}
if (arr[e4] < arr[e3]) {
int t = arr[e4]; arr[e4] = arr[e3]; arr[e3] = t;
if (t < arr[e2]) {
arr[e3] = arr[e2]; arr[e2] = t;
if (t < arr[e1]) {
arr[e2] = arr[e1]; arr[e1] = t;
}
}
}
if (arr[e5] < arr[e4]) {
int t = arr[e5]; arr[e5] = arr[e4]; arr[e4] = t;
if (t < arr[e3]) {
arr[e4] = arr[e3]; arr[e3] = t;
if (t < arr[e2]) {
arr[e3] = arr[e2]; arr[e2] = t;
if (t < arr[e1]) {
arr[e2] = arr[e1]; arr[e1] = t;
}
}
}
}
//建立左右指针
int less = left;
int great = right;
//如果arr[e1] < arr[e2] < arr[e3] < arr[e4] < arr[e5],进行双轴排序
if (arr[e1] != arr[e2] && arr[e2] != arr[e3] && arr[e3] != arr[e4] && arr[e4] != arr[e5]) {
//建立左右轴
int pivot1 = arr[e2];
int pivot2 = arr[e4];
arr[e2] = arr[left];
arr[e4] = arr[right];
while (pivot1 > arr[++less]) ;
while (pivot2 < arr[--great]) ;
/*
* 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.
*/
outer:
for (int k = less - 1; ++k <= great; ) {
int ak = arr[k];
if (ak < pivot1) {
arr[k] = arr[less];
arr[less] = ak;
less++;
} else if (ak > pivot2) {
while (pivot2 < arr[great]) {
if (great-- == k) {
break outer;
}
}
if (pivot1 > arr[great]) {
arr[k] = arr[less];
arr[less] = arr[great];
less++;
} else {
arr[k] = arr[great];
}
arr[great]