[排序演算法]Merge sort — 合併排序法

Merge Sort 採用分治法(Divide and Conquer)的方式來處理排序的問題,簡單介紹一下分治法執行的步驟如下:

  1. Divide:先將大問題不斷切割成小問題
  2. Conquer:用遞迴的方式處理所有的子問題
  3. Combine:將所有的結果合併起來就是最終解答

實作步驟:

將陣列不斷分割,分割到只有一個元素為止,再依照大小依序組合起來,流程可參考下圖

用 js 實作 merge sort

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
const merge = (arr1, arr2) => {
const result = [];
//用雙指針來紀錄比較位置
let i = 0;
let j = 0;

//兩個陣列裡的元素逐一比較大小
while (i < arr1.length && j < arr2.length) {
if (arr1[i] > arr2[j]) {
result.push(arr2[j]);
j++;
} else {
result.push(arr1[i]);
i++;
}
}

//將剩餘的元素放入result
while (i < arr1.length) {
result.push(arr1[i]);
i++;
}
while (j < arr2.length) {
result.push(arr2[j]);
j++;
}
return result;
};

const mergeSort = (arr) => {
if (arr.length === 1) return arr;
//找中間點
let middle = Math.floor(arr.length / 2);
//將陣列切為左半邊和右半邊
let left = arr.slice(0, middle);
let right = arr.slice(middle, arr.length);
return merge(mergeSort(right), mergeSort(left));
};

mergeSort([8, 3, 6, 7, 2]);

這邊畫流程圖來講解 merge 這個函式在做的事情

  1. 假設傳入兩個陣列個別是 arr1 和 arr2 (這兩個必定是已經排序好的陣列)

2. 第一個 while 迴圈會搭配雙指針將兩個陣列相互比大小,先取兩個陣列中的第一個來做比較,1 比 2 小所以放入 result 裡面,然後本來指向 1 的 j 指針就要往後移動一格,指向 4

3. 2 與 4 做比較,2 比較小所以放入 result 中,本來指向 2 的 i 指針就要往後移動一格,指向 3

4. 經過不斷的比較會發現 i 指針已經指到最後一個了,但是 j 指針還沒到終點,此時第一個 while 迴圈已經終止,可以發現 arr1 的已經全部被放進 result 裡面了,arr2 還有兩個

5.因此會執行第三個 while 迴圈,將 arr2 剩下的元素放進 result,如果今天是 arr1 有剩餘的元素就會執行第二個 while 迴圈,最後可以得到一個合併好的陣列並且排序由小到大

空間複雜度相較其它排序演算法會比較高,因為會不斷切割生成新的陣列,需要額外的記憶體空間來存放

時間複雜度

👍 在最差的情況下,時間複雜度是 O(n log n)

👎 在最佳的情況下,時間複雜度是 O(n log n)

🤚 在平均情況下,時間複雜度為 O(n log n)

參考資料: divide-and-conquer