归并排序原理:将数组二分,然后对二分后的数组继续使用归并排序...当分解后数组元数足够小时(为1),说明每个数组已经排序好,然后将二分的数组通过某个算法合并成已经排好序的数组,直至整个数组都排序好序。
时间复杂度:最优O(n),最坏O(nlogn),平均O(nlogn)
归并排序jdk源码优化方案:
1. 当分解后的数组元素个数小于7时,改用插入排序
2. 当二分后的已经排好序的两个数组,左边的最大值小于等于右边最小值时,不需要再合并
3. 复制一份源数组,在归并两个已排序数组时,只要将源数据按顺序移到目标数组中,可以避免在同一个数组整体移动产生开销,达到线性时间,以空间换时间
public static final int INSERTIONSORT_THRESHOLD = 7;
public static <T extends Comparable<T>> int compare(T a, T b) {
if (a == null){
return b == null ? 0 : -1;
}
if (b == null){
return 1;
}
return a.compareTo(b);
}
/**
* @description 归并排序
* @param src 待排数组
* @param dest 排序后的数组
* @param offset 开始位置
* @param end 结束位置
*/
public static <T extends Comparable<T>> void mergeSort(T[] src, T[] dest, int offset, int end) {
int size = end - offset;
//当数组元素个数小于7时,插入排序性能最好
if (size < INSERTIONSORT_THRESHOLD) {
insertSort(dest, offset, end);
return;
}
int mid = (offset + end) >>> 1;
//递归将左半部分未排序的数据,排好序后放入源数组中
mergeSort(dest, src, offset, mid);
//递归将右半部分未排序的数据,排好序后放入源数组中
mergeSort(dest, src, mid, end);
//左边最大数比右边最小数小,说明数组已经排好序,直接拷贝到目标数组中
if (compare(src[mid - 1], src[mid]) <= 0) {
System.arraycopy(src, offset, dest, offset, size);
return;
}
//按顺序将源数据拷贝到目标数组中
for (int i = offset, p = i, q = mid; i < end; ++i) {
if (q >= end || p < mid && compare(src[p], src[q]) <= 0) {
dest[i] = src[p++];
} else {
dest[i] = src[q++];
}
}
}
/**
* @description 归并排序
* @param data
*/
public static <T extends Comparable<T>> void mergeSort(T[] data) {
if (data != null && data.length > 1){
//复制一份数组做交换空间
T[] src = (T[]) data.clone();
mergeSort(src, data, 0, data.length);
}
}