查看完整的代码,点击这里
不了解归并排序的可以查看百度百科的分析
归并排序的实现
基本实现
package main
import "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)
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。