本篇將為大家介紹合併排序 (Merge Sort) 的原理、虛擬碼、程式碼與時間/空間複雜度分析。
合併排序
是外部排序 (External Sorting) 常用的排序方法之一,採用 “Divide-and-Conquer” 策略。
Devide-and-Conquer 步驟:
- Devide: 將原問題分成若干個仔問題
- Conquer: 遞迴解決各個子問題;當子問題夠小時則直接解
- Combine: 將子問題的解合併成原問題的解
Merge Sort 是 Devide-and-Conquer 相當著名的應用之一,步驟如下:
- 將原本的 Data List 切割成兩等分
- 將左、右的 Sublist 各自以 Merge Sort 排序
- 合併左右半部的兩個 Sublist 成為一個新的 Data List
虛擬碼 (Pseudo Code)
Merge(arr_1[], arr_2[]){
p 為 arr_1[]的起始 index;
q 為 arr_2[]的起始 index;
while(arr_1 && arr_2 尚未scan完畢){
if (p.data < q.data) then{
p.data 先輸出到一個新的 array;
p = p + 1;
}
else{
q.data 先輸出到一個新的 array;
q = q + 1;
}
while(arr_1尚未scan完畢){
複製 arr_1 剩下的 data 到新的 array 裡;
}
while(arr_2尚未scan完畢){
複製 arr_2 剩下的 data 到新的 array 裡;
}
}
}
MergeSort(A[], head, tail){
if(head < tail){
mid = (head + tail) / 2;
MergeSort(arr, head, mid);
MergeSort(arr, mid+1, tail);
Merge(arr, head, mid, tail);
}
}
程式碼
#include <stdio.h>
#include <stdlib.h>
// Merge two subarrays of A[].
// First subarray is arr[head..mid]
// Second subarray is arr[mid+1..tail]
void merge(int arr[], int head, int mid, int tail){
int lenA = mid - head + 1;
int lenB = tail - (mid + 1) + 1;
int A[lenA];
int B[lenB];
//Copy data to temp arrays A[] and B[]
int i, j, k;
for(i = 0; i < lenA; i++){
A[i] = arr[head + i];
}
for(j = 0; j < lenB; j++){
B[j] = arr[mid + 1 + j];
}
// Merge two temp arrays back into arr[head..tail]
i = 0;
j = 0;
k = head;
//while array A and B haven't finished scanning
while(i < lenA && j < lenB){
if(A[i] < B[j]){
arr[k] = A[i];
i++;
}
else{
arr[k] = B[j];
j++;
}
k++;
}
//Copy the remaing elements into arr[], if A[] haven't finished scanning
while(i < lenA){
arr[k] = A[i];
i++;
k++;
}
//Copy the remaing elements into arr[], if B[] haven't finished scanning
while(j < lenB){
arr[k] = B[j];
j++;
k++;
}
}
void merge_sort(int arr[], int head, int tail){
if(head < tail){
int mid = (head + tail) / 2;
merge_sort(arr, head, mid);
merge_sort(arr, mid+1, tail);
merge(arr, head, mid, tail);
}
}
int main(){
int count, i;
scanf("%d", &count);
int list[count];
printf("Numbers to be sorted: ");
for(i = 0; i<count; i++){
scanf("%d", &list[i]);
printf("%d ", list[i]);
}
printf("\n");
merge_sort(list, 0, count-1);
printf("Numbers Sorted: ");
for(i = 0; i<count; i++){
printf("%d ", list[i]);
}
return 0;
}

時間複雜度 (Time complexity)
如果 Sublist A 的長度為 m、Sublist B 的長度為 n,則合併兩個 Sublist:
- 最少比較次數 = m 或 n (若將 A 長度改為 n,則為 n)
- 最多比較次數 = m + n – 1 次 (若將 A 長度改為 n,則為 2n-1)
=> 時間皆為 O(n)

已知每回合 merge 所花費的時間為 O(n),則 n 個 Data 做 Merge Sort 需要幾個回合?
由於 Merge Sort 過程相當於一棵 Binary Tree,故 Merge Sort 的回合數相當於「Binary Tree 的高度 – 1」。
n 個 Data 的 Binary Tree 高度為 log2 n + 1,故回合數為 log2 n。
- Merge Sort 的總時間
- = 回合數 * 每回合所花的時間
- = log n * O(n)
- = O(n logn)
Average/Best/Worst Case 的時間皆為 O(n logn)。
改用 Recursive Time Function 運算仍可得相同結果:
T(n) = T(n/2) + T(n/2) + c*n //c為正常數, c*n為合併的時間 = 2*T(n/2) + c*n => T(n) = O(n logn)
空間複雜度 (Space Complexity)
需要一個與原來 Data List 一樣的額外空間,來暫時儲存每一回合的合併結果,故為 O(n)。
穩定性:Stable
array A | array B
5 , 8 | 5* , 10
merge
5 , 5* , 8 , 10
可知 5 和 5* 的位置在合併後仍不會前後交換。










