1 回答
TA贡献1780条经验 获得超4个赞
我想建议几件事:
数组范围。Dijkstra 曾经争论过数组范围(或在 Go 中,切片范围)应该如何:对于 的符号
l[i:j]
,您希望它具有所有这些属性:它应该从 i 开始。
计算长度应该是微不足道的:
len(l[i:j]) == j-i
总是正确的表达一个空范围应该是优雅的,所以
i<=j
总是如此
因此,l[i:j]被设置为一个半开范围:[i,j],包含下界,排除上界。这也是 Go 切片的工作方式(以及 Python 和许多其他语言)。
关键是,最好在您的代码中保留此约定:在执行范围时,包括下限并排除上限。
切片是内置于 Go 中的。你可以使用它而且很便宜。您不需要以如此冗长且容易出错的方式计算所有这些
l
,r
,mid
您只需要将slice
.
例如:
func mergeSort(arr []int) {
size := len(arr)
if size <= 1 {
return
}
mid := size / 2
mergeSort(arr[:mid])
mergeSort(arr[mid:])
merge(arr, arr[:mid], arr[mid:])
}
代码更清晰、更健壮。
切片不做深拷贝,这意味着,
left := arr[l:mid]
只创建一个指向 的元素的指针arr
。这就是为什么我说切片在 Go 中很便宜。
但是,如果没有深拷贝,当您合并切片时,数据会被覆盖并因此损坏。您需要合并到一个新的切片中,然后将其复制回原始切片。这就是为什么 naive mergesort 被认为有O(n)
额外的内存使用。
func merge(arr, left, right []int) {
res := make([]int, len(arr))
leftSize, rightSize := len(left), len(right)
var i,j,k int
for i = range res {
if j >= leftSize || k >= rightSize {
break
}
if left[j] <= right[k] {
res[i] = left[j]
j++
} else {
res[i] = right[k]
k++
}
}
// Only one of these two copies run, so no need to increase i
copy(res[i:], left[j:])
copy(res[i:], right[k:])
copy(arr, res)
}
Playground: https://play.golang.org/p/LlJj-JycfYE
- 1 回答
- 0 关注
- 144 浏览
添加回答
举报