介绍
归并排序是一种分治策略的排序算法。它是一种比较特殊的排序算法,通过递归地先使每个子序列有序,再将两个有序的序列进行合并成一个有序的序列。
归并排序首先由著名的现代计算机之父 John_von_Neumann 在 1945 年发明,被用在了 EDVAC(一台美国早期电子计算机),足足用墨水写了 23 页的排序程序。注:冯·诺依曼(John von Neumann,1903年12月28日-1957年2月8日),美籍匈牙利数学家、计算机科学家、物理学家,是20世纪最重要的数学家之一。
执行流程
我们先介绍两个有序的数组合并成一个有序数组的操作。
- 先申请一个辅助数组,长度等于两个有序数组长度的和。
- 从两个有序数组的第一位开始,比较两个元素,哪个数组的元素更小,那么该元素添加进辅助数组,然后该数组的元素变更为下一位,继续重复这个操作,直至数组没有元素。
- 返回辅助数组。
时间复杂度
归并操作最坏的时间复杂度为:O(n),其中 n 是较长数组的长度。
归并操作最好的时间复杂度为:O(n),其中 n 是较短数组的长度。
代码实现
// 自顶向下归并排序,排序范围在 [begin,end) 的数组
func SortMerge(array []int, begin int, end int) {
// 元素数量大于1时才进入递归
if end-begin > 1 {
// 将数组一分为二,分为 array[begin,mid) 和 array[mid,high)
mid := begin + (end-begin+1)/2
// 先将左边排序好
SortMerge(array, begin, mid)
// 再将右边排序好
SortMerge(array, mid, end)
// 两个有序数组进行合并
merge(array, begin, mid, end)
}
}
// 归并操作
func merge(array []int, begin int, mid int, end int) {
// 申请额外的空间来合并两个有序数组,这两个数组是 array[begin,mid),array[mid,end)
leftSize := mid - begin // 左边数组的长度
rightSize := end - mid // 右边数组的长度
newSize := leftSize + rightSize // 辅助数组的长度
result := make([]int, 0, newSize)
l, r := 0, 0
for l < leftSize && r < rightSize {
lValue := array[begin+l] // 左边数组的元素
rValue := array[mid+r] // 右边数组的元素
// 小的元素先放进辅助数组里
if lValue < rValue {
result = append(result, lValue)
l++
} else {
result = append(result, rValue)
r++
}
}
// 将剩下的元素追加到辅助数组后面
result = append(result, array[begin+l:mid]...)
result = append(result, array[mid+r:end]...)
// 将辅助数组的元素复制回原数组,这样该辅助空间就可以被释放掉
for i := 0; i < newSize; i++ {
array[begin+i] = result[i]
}
return
}
测试
func TestSortMerge(t *testing.T) {
list := []int{5, 9, 1, 6, 8, 14, 6, 49, 25, 4, 6, 3}
SortMerge(list,0,len(list))
fmt.Println(list)
list = []int{-1, 5, 9, 1, 6, 8, 14, 6, 49, 25, 4, 6, 3}
SortMerge(list,0,len(list))
fmt.Println(list)
list = []int{-1, 11, 22, 33}
SortMerge(list,0,len(list))
fmt.Println(list)
}
测试结果
=== RUN TestSortMerge
[1 3 4 5 6 6 6 8 9 14 25 49]
[-1 1 3 4 5 6 6 6 8 9 14 25 49]
[-1 11 22 33]
--- PASS: TestSortMerge (0.00s)
PASS