一、原理简介
归并排序又称合并排序,即针对两个已经排序好的数组,将其合并为一个排序数组。归并过程中,依次比较两个数组中的元素,将较小值(假设为升序排序)放入新数组中,最终遍历完两个数组后完成排序。
归并排序是一种速度较快的排序方法。
二、时间复杂度
当我们合并 两个已排序的数组,只需要遍历一遍即可,其时间复杂度为 O(n)。
当我们对一个 无序数组 使用归并排序法进行排序,我们可以使用分治法将数组进行对半划分,递归解决。由于进行二分的时间复杂度为 O(lg n),单次合并(即合并两个已排序的数组)操作时间复杂度为 O(n),所以综合时间复杂度为 O(n * lg n)。
三、合并两个已排序的数组
这种情况时间复杂度为 O(n)。因为我们只需要遍历一遍。
1. 两个数组合并为一个
public class Main {
public static void main(String[] args) {
// 前提:arr1 和 arr2 都是已排序的
int[] arr1 = new int[] { 3, 15, 23, 76, 100 };
int[] arr2 = new int[] { 1, 16, 18, 55, 66 };
int[] arr = mergeSort(arr1, arr2);
printArray(arr);
}
public static int[] mergeSort(int[] arr1, int[] arr2) {
int[] arr = new int[arr1.length + arr2.length];
int i = 0; // arr1当前index
int j = 0; // arr2当前index
int k = 0; // arr当前index
while (i < arr1.length && j < arr2.length) {
if (arr1[i] < arr2[j]) {
arr[k++] = arr1[i++];
} else {
arr[k++] = arr2[j++];
}
}
if (i != arr1.length) {
System.arraycopy(arr1, i, arr, k, arr1.length - i);
} else {
System.arraycopy(arr2, j, arr, k, arr2.length - j);
}
return arr;
}
public static void printArray(int[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
}
}
2. 一个数组,左右两段是已排序的
和上例类似,只是 arr1 和 arr2 可以视为这一个数组的左右两部分。
public class Main {
public static void main(String[] args) {
// 前提:arr 的左半部分[0, 5)和右半部分[5, 10)均已排序。区间均为左闭右开
int[] arr = new int[] { 1, 3, 5, 7, 9, 2, 4, 6, 8, 10 };
mergeSort(arr, 0, 5, 10);
printArray(arr);
}
/**
* 归并排序
* @param arr 目标数组,满足 p < q≤ r,且区间 [p...q) 和 [q...r) 均已排好序
* @param p 起始位置
* @param q 中间位置
* @param r 结束位置
*/
public static void mergeSort(int[] arr, int start, int pivot, int end) {
int[] temp = new int[end - start];
int i = start;
int j = pivot;
int k = 0;
while (i < pivot && j < end) {
if (arr[i] < arr[j]) {
temp[k++] = arr[i++];
} else {
temp[k++] = arr[j++];
}
}
if (i != pivot) {
System.arraycopy(arr, i, temp, k, pivot - i);
} else {
System.arraycopy(arr, j, temp, k, end - j);
}
System.arraycopy(temp, 0, arr, start, temp.length);
}
public static void printArray(int[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
}
}
四、无序数组排序
这种情况时间复杂度为 O(n * lg n)。
我们需要对数组折半划分,直至其足够小时(例如数组长度为 1),可视为左右两个数组是已排序的(长度为 1 的数组可以视为已排序的),然后分别执行上面的合并操作。递归执行下去,最终完成整个数组的排序。
public class Main {
public static void main(String[] args) {
int[] arr = new int[] { 2, 3, 8, 6, 1 };
mergeSort(arr, 0, arr.length);
printArray(arr);
}
public static void mergeSort(int[] arr, int start, int end) {
int length = end - start;
if (length > 1) { // 长度大于1才需要排序
int mid = (start + end) / 2;
mergeSort(arr, start, mid); // sort left
mergeSort(arr, mid, end); // sort right
merge(arr, start, mid, end); // merge left and right
}
}
public static void merge(int[] arr, int start, int mid, int end) {
// check input
if (arr == null || start < 0 || end > arr.length) {
return;
}
int[] temp = new int[end - start];
int i = start;
int j = mid;
int k = 0;
while (i < mid && j < end) {
if (arr[i] <= arr[j]) {
temp[k++] = arr[i++];
} else {
temp[k++] = arr[j++];
}
}
if (i != mid) {
System.arraycopy(arr, i, temp, k, mid - i);
}
if (j != end){
System.arraycopy(arr, j, temp, k, end - j);
}
System.arraycopy(temp, 0, arr, start, temp.length);
}
public static void printArray(int[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
}