詳細描述
歸併排序的基本思想是,將已有序的子序列合併,可以得到有序的完整序列;即先使每個子序列有序,再使子序列段間有序。若將兩個有序表合併成一個有序表,稱為二路歸併。
歸併排序詳細的執行步驟如下:
- 申請空間,使其大小為兩個已經排序序列之和,該空間用來存放合併後的序列;
- 設定兩個指標,最初位置分別為兩個已經排序序列的起始位置;
- 比較兩個指標所指向的元素,選擇相對小的元素放入到合併空間,並移動指標到下一位置;
- 重複步驟 3 直到某一指標超出序列尾。
演算法圖解
問題解疑
歸併排序和快速排序比較怎麼樣?
歸併排序的比較次數小於快速排序的比較次數,移動次數一般多於快速排序的移動次數。
歸併排序的應用在什麼場景?
歸併排序速度僅次於快速排序,為穩定排序演算法,一般用於對總體無序,但是各子項相對有序的序列。
歸併排序的時間複雜度是多少?
每次合併操作的平均時間複雜度為 \(O(n)\),而完全二叉樹的深度為 \(\log_2n\),總的平均時間複雜度為 \(O(n \log n)\)。
並且,歸併排序的最好、最壞、平均時間複雜度均為 \(O(n \log n)\)。
程式碼實現
package cn.fatedeity.algorithm.sort;
/**
* 歸併排序演算法
*/
public class MergeSort {
private static void merge(int[] numbers, int low, int mid, int high) {
int i = low;
int j = mid + 1;
int[] newNumbers = new int[high - low + 1];
int k = 0;
while (i <= mid && j <= high) {
if (numbers[i] < numbers[j]) {
newNumbers[k++] = numbers[i++];
} else if (numbers[i] > numbers[j]) {
newNumbers[k++] = numbers[j++];
} else {
newNumbers[k++] = numbers[i++];
newNumbers[k++] = numbers[j++];
}
}
while (i <= mid) {
newNumbers[k++] = numbers[i++];
}
while (j <= high) {
newNumbers[k++] = numbers[j++];
}
if (high + 1 - low >= 0) {
System.arraycopy(newNumbers, 0, numbers, low, high + 1 - low);
}
}
private static int[] sort(int[] numbers, int low, int high) {
if (low < high) {
int mid = (low + high) >> 1;
sort(numbers, low, mid);
sort(numbers, mid + 1, high);
merge(numbers, low, mid, high);
}
return numbers;
}
public static int[] sort(int[] numbers) {
return sort(numbers, 0, numbers.length - 1);
}
}