查看完整的代碼,點擊這裡
不瞭解歸併排序的可以查看百度百科的分析
歸併排序的實現
基本實現
package mainimport "fmt"// 合并 [l,r] 兩部分資料,mid 左半部分的終點,mid + 1 是右半部分的起點func merge(arr []int, l int, mid int, r int) { // 因為需要直接修改 arr 資料,這裡首先複製 [l,r] 的資料到新的數組中,用於賦值操作 temp := make([]int, r-l+1) for i := l; i <= r; i++ { temp[i-l] = arr[i] } // 指向兩部分起點 left := l right := mid + 1 for i := l; i <= r; i++ { // 左邊的點超過中點,說明只剩右邊的資料 if left > mid { arr[i] = temp[right-l] right++ // 右邊的資料超過終點,說明只剩左邊的資料 } else if right > r { arr[i] = temp[left-l] left++ // 左邊的資料大於右邊的資料,選小的數字 } else if temp[left - l] > temp[right - l] { arr[i] = temp[right - l] right++ } else { arr[i] = temp[left - l] left++ } }}func MergeSort(arr []int, l int, r int) { if l >= r { return } // 遞迴向下 mid := (r + l) / 2 MergeSort(arr, l, mid) MergeSort(arr, mid+1, r) // 歸併向上 merge(arr, l, mid, r)}func main() { arr := []int{3, 1, 2, 5, 6, 43, 4} MergeSort(arr, 0, len(arr)-1) fmt.Println(arr)}
最佳化點
- 只有左邊資料的最大值大於右邊資料的最小值時才需要歸併
舉例:現在需要歸併的左右部分為 [1,3] 和 [4,5],則不需要進行「並」的操作。而 [1,4] 和 [3,5] 則需要進行「並」為 [1,3,4,5]
- 當二分資料到一定階段,可以使用插入排序而不是繼續向下二分
雖然複雜度上看 nlogn 始終大於 n^2,但是都忽略了常數項,而歸併的常數項大於插入排序,所以當 n 足夠小時,插入排序速度更快
以下是結合最佳化點的程式更改:
package main import "fmt" func merge(arr []int, l int, mid int, r int) { temp := make([]int, r-l+1) for i := l; i <= r; i++ { temp[i-l] = arr[i] } left := l right := mid + 1 for i := l; i <= r; i++ { if left > mid { arr[i] = temp[right-l] right++ } else if right > r { arr[i] = temp[left-l] left++ } else if temp[left - l] > temp[right - l] { arr[i] = temp[right - l] right++ } else { arr[i] = temp[left - l] left++ } }} func MergeSort(arr []int, l int, r int) { // 第二步最佳化,當資料規模足夠小的時候,可以使用插入排序 if r - l <= 15 { // 對 l,r 的資料執行插入排序 for i := l + 1; i <= r; i++ { temp := arr[i] j := i for ; j > 0 && temp < arr[j-1]; j-- { arr[j] = arr[j-1] } arr[j] = temp } return } mid := (r + l) / 2 MergeSort(arr, l, mid) MergeSort(arr, mid+1, r) // 第一步最佳化,左右兩部分已排好序,只有當左邊的最大值大於右邊的最小值,才需要對這兩部分進行merge操作 if arr[mid] > arr[mid + 1] { merge(arr, l, mid, r) }} func main() { arr := []int{3, 1, 2, 5, 6, 43, 4} MergeSort(arr, 0, len(arr)-1) fmt.Println(arr)}