归并排序(递归版本)
首先是递归版本的归并排序,不做多的解释了,直接上代码。
// MergeSort [l, r)
func MergeSort(arr []int, l int, r int) {
if l+1 >= r {
return
}
mid := l + (r-l)>>1
MergeSort(arr, l, mid)
MergeSort(arr, mid, r)
merge(arr, l, r)
}
// merge [l, r)
func merge(arr []int, l int, r int) {
var help []int
mid := l + (r-l)>>1
i, j := l, mid
for i < mid && j < r {
if arr[i] < arr[j] {
help = append(help, arr[i])
i++
} else {
help = append(help, arr[j])
j++
}
}
for i < mid {
help = append(help, arr[i])
i++
}
for j < r {
help = append(help, arr[j])
j++
}
// copy help to arr
copy(arr[l:r], help)
}
递归版本时间复杂度证明
两边同时除以 (这里的 代表数据的样本总量)
易得
......
最后可得
将上述式子累加,得到
即
故
可得归并排序的时间复杂度为 。
归并排序(非递归版本)
这里主要是利用步长 step
实现非递归。
对于非递归版本,我们假设对于如下这个数组,进行到步长为2的情况下,如图所示
进行局部归并得到
再移动指针进行下一次归并
接下来步长变为4,继续归并数组,然后步长变为 8, 16 ...,到这里是不是就明白了,跟归并排序的递归版本一模一样。
直到归并的步长能够做到一次归并就把 [0, step)
和 [step, step + step)
排序完成(这里的 step + step
可能会大于数组的长度,所以我们需要稍作判断),我们就完成了整个数组的排序。
// MergeSortStep [l, r)
// Non-recursive method implementation
func MergeSortStep(arr []int, l int, r int) {
step := 1
for step < r-l {
left, right := 0, step
// merge [left, left+step) and [right, right+step)
for left < r {
var help []int
i, j := left, right
for i < left+step && j < right+step && j < r {
if arr[i] < arr[j] {
help = append(help, arr[i])
i++
} else {
help = append(help, arr[j])
j++
}
}
for i < left+step && i < r {
help = append(help, arr[i])
i++
}
for j < right+step && j < r {
help = append(help, arr[j])
j++
}
if i == r {
copy(arr[left:i], help)
} else {
copy(arr[left:j], help)
}
left, right = left+step*2, right+step*2
}
// Prevent the value from exceeding the maximum value of int
if step > (r-l)>>1 {
break
}
step <<= 1
}
}
非递归版本时间复杂度证明
由于步长从1开始变化,每次乘以2,退出条件是 step < n
,所以我们很容易得到外层循环执行了 次,内层循环每次都要完整的遍历一次数组,故总的时间复杂度为 。