归并排序(Merge Sort)
基本思想
归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。
算法描述
①把长度为n的输入序列分成两个长度为n/2的子序列;
②对这两个子序列分别采用归并排序;
③将两个排序好的子序列合并成一个最终的排序序列。
动图演示
代码实现
public static int[] mergeSort(int[] arr) {
if (arr == null || arr.length < 2) {
return arr;
}
return mergeSort(arr, 0, arr.length - 1);
}
/**
* 功能描述:递归方法,分别对数组左右两侧进行归并排序
*/
public static int[] mergeSort(int[] arr, int l, int r) {
if (l == r) {
return arr;
}
//L和R的中点位置,(L+R)/2
int mid = l + ((r - l) >> 1);
//左侧排序
mergeSort(arr, l, mid);
//右侧排序
mergeSort(arr, mid + 1, r);
//整合
return merge(arr, l, mid, r);
}
/**
* 功能描述:数组左右两部分排好序,进行整合
*/
public static int[] merge(int[] arr, int l, int m, int r) {
//保持范围内数组个数一致 r - l + 1
int[] help = new int[r - l + 1];
int i = 0;
//指针分别指向左右两侧的第一个数
int p1 = l;
int p2 = m + 1;
//谁小填谁,跳出这个循环,有且只有一侧必定越界
while (p1 <= m && p2 <= r) {
//填完的数,到下一个位置+1
help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
}
//两个循环走一个,将不越界的填进辅助数组
while (p1 <= m) {
help[i++] = arr[p1++];
}
while (p2 <= r) {
help[i++] = arr[p2++];
}
//辅助数组拷贝给原数组
for (i = 0; i < help.length; i++) {
//保证传递给原数组是从l开始的
arr[l + i] = help[i];
}
return arr;
}
分析
稳定性
归并排序是稳定的。因为在使用额外空间的时候,靠前区域的元素只要小于等于靠后区域的元素就能被放进额外空间。
时间复杂度
任何情况下 T(n) = O(nlogn)。归并排序主要是靠递归加二分法,每一层的时间代价都是 n 相关,一共有 logn 层,所以时间复杂度是 n*logn。所以无论数组是不是有序的,都会被二分法分为前后两个部分,然后递归下去。