归并排序和快排一样使用的是分治的思想,所以也可以提供很好的性能,但是归并可以提供比快排更稳定的性能,以及归并排序本身是稳定的,所以被很多语言用在对象排序方式上,java的Arrays.sort方法的对象排序用的就是归并排序的一种
扩展:在jdk1.8之后,Arrays.sort的对象排序在某个阈值下使用的二分插入排序,当元素个数大于阈值的时候使用的是一种改进的归并排序TimSort,中文名叫多路归并
Arrays.sort的基本类型用的是一种改进的快排
我这里以从小到大排序为例:
基本的归并排序是将待排数组拆成两等分的子数组,然后再将子数组拆成两等分的孙子数组,直到拆成数组中只有一个元素的时候停止拆分,然后再进行两两合并,合并的时候同时遍历两个子数组的元素并进行比较,每次将两个数组中较小的元素弹出并按顺序放回原数组,直到其中一个数组没有元素了,已知两个子数组已经各自有序了,那么另外一个子数组剩下的元素一定都比已经填入原数组的元素大,此时就不再需要比较,直接挨个按顺序填回原数组就可以了,合并完成后的数组就是有序数组,然后再将这个数组合并到更大的数组中,直到整个数组全部合并完毕,排序完成
归并在的数据量下拥有比快排更稳定的性能
中文名称 | 英文名称 | 平均时间复杂度 | 最坏时间复杂度 | 最好时间复杂度 | 空间复杂度 | 稳定性 |
---|---|---|---|---|---|---|
归并排序 | Merge | n*logn | n*logn | n*logn | n | 稳定 |
private static void merge(int arr[], int left, int mid, int right){
//我们将原数组需要合并的部分直接合并到这个数组,然后再将这个数组覆盖回去,这样只创建一个临时数组就够用了
int[] tempArr = new int[right - left + 1];
//l是第一个子数组的下标,r是第二个子数组的下标,i是临时数组的下标
int l = left,r = mid,i = 0;
//将两个子数组的元素按顺序插入到临时数组中
while (l < mid && r <= right){
if(arr[l] <= arr[r]){
tempArr[i] = arr[l];
i++;
l++;
}else{
tempArr[i] = arr[r];
i++;
r++;
}
}
//如果有哪个数组仍然有元素没有插入到临时数组中,那么直接全部插入
if(l < mid){
for (int j = l; j < mid; j++,i++){
tempArr[i] = arr[j];
}
}
if(r <= right){
for (int j = r; j <= right; j++,i++){
tempArr[i] = arr[j];
}
}
//把排好序的临时数组覆盖回原数组对应的位置
for (int j = 0; j < tempArr.length && left <= right; j++,left++) {
arr[left] = tempArr[j];
}
}
private static void sort(int arr[], int left, int right){
if(left==right){
return;
}
//拆拆拆,然后递归递归递归
int mid = (left + right) / 2;
sort(arr, left, mid);
sort(arr, mid+1, right);
merge(arr, left, mid+1, right);
}
public static void sort(int arr[]){
sort(arr, 0, arr.length - 1);
}