Java 歸併排序法
阿新 • • 發佈:2018-12-13
一、原理簡介
歸併排序又稱合併排序,即針對兩個已經排序好的陣列,將其合併為一個排序陣列。歸併過程中,依次比較兩個陣列中的元素,將較小值(假設為升序排序)放入新陣列中,最終遍歷完兩個陣列後完成排序。
歸併排序是一種速度較快的排序方法。
二、時間複雜度
當我們合併 兩個已排序的陣列,只需要遍歷一遍即可,其時間複雜度為 O(n)。
當我們對一個 無序陣列 使用歸併排序法進行排序,我們可以使用分治法將陣列進行對半劃分,遞迴解決。由於進行二分的時間複雜度為 O(log n),單次合併(即合併兩個已排序的陣列)操作時間複雜度為 O(n),所以綜合時間複雜度為 O(n * log 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 * log 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();
}
}