JDK1.8中的数组排序
Array.sort()
核心思路
根据不同的情况,选择不同的排序算法。
1、当需要排列的元素较少的时候,采用普通的插入排序
当被排序的数组长度小于47但排序不是从数组起始位置开始的时候,那么就会选择哨兵插入排序的方式进行排序。
这种情况主要是当sort被双基准快排递归调用的时候才会使用
2、如果元素较多,大于插入排序的阈值,但是小于归并排序的阈值,这时采用快速排序
在进行快排之前,首先会将这个不长不断的数组按照1/7的长度划分,最后划分后的结果为:
// Inexpensive approximation of length / 7
int seventh = (length >> 3) + (length >> 6) + 1;
/*
* Sort five evenly spaced elements around (and including) the
* center element in the range. These elements will be used for
* pivot selection as described below. The choice for spacing
* these elements was empirically determined to work well on
* a wide variety of inputs.
*/
int e3 = (left + right) >>> 1; // The midpoint
int e2 = e3 - seventh;
int e1 = e2 - seventh;
int e4 = e3 + seventh;
int e5 = e4 + seventh;
然后对这5个划分点进行排序:
2.1 如果这五个划分点的数据俩俩各不相同,则以第一个划分点和最后一个划分点作为基准点,采用双基准快排;
采用双基准快排也有优化的地方,如果中间的元素过多(超过整个排序部分的七分之四),将会针对这一区间内的重复键进行优化。
与基准1和基准2 相同的键将会被分到前后两个分区,并会被从接下来要继续递归排序的中间分区中剔除,尽可能减少了中间分区的大小。
for (int k = less - 1; ++k <= great; ) {
int ak = a[k];
if (ak == pivot1) { // Move a[k] to left part
a[k] = a[less];
a[less] = ak;
++less;
} else if (ak == pivot2) { // Move a[k] to right part
while (a[great] == pivot2) {
if (great-- == k) {
break outer;
}
}
if (a[great] == pivot1) { // a[great] < pivot2
a[k] = a[less];
/*
* Even though a[great] equals to pivot1, the
* assignment a[less] = pivot1 may be incorrect,
* if a[great] and pivot1 are floating-point zeros
* of different signs. Therefore in float and
* double sorting methods we have to use more
* accurate assignment a[less] = a[great].
*/
a[less] = pivot1;
++less;
} else { // pivot1 < a[great] < pivot2
a[k] = a[great];
}
a[great] = ak;
--great;
}
}
2.2 如果这五个划分点有相同的情况,则采用三路快排。
3、如果数组的长度超过了快排的阈值
首先进行有序性的判断,判断这个数组是否是基本有序
将有序部分中断处的位置记录下来,如果中断个数大于某个阈值则判断为无序,反之有序。
3.1 如果是基本有序,则采用归并方式排序,这里会按照刚才记录的有序部分的中断点作为归并点。
3.2 如果判定为无序,则依然采用快排。