quicksort 是原地排序函数,递归实现:若长度≤1则返回;选首元素为 pivot,分区后递归排序左右子数组。

Go 里手写 quicksort 最简可行版本
直接上能跑、不依赖标准库、边界处理正确的实现:
func quicksort(arr []int) {
if len(arr) <= 1 {
return
}
pivot := partition(arr)
quicksort(arr[:pivot])
quicksort(arr[pivot+1:])
}
<p>func partition(arr []int) int {
pivot := len(arr) - 1
i := 0
for j := 0; j < pivot; j++ {
if arr[j] <= arr[pivot] {
arr[i], arr[j] = arr[j], arr[i]
i++
}
}
arr[i], arr[pivot] = arr[pivot], arr[i]
return i
}这个版本用末尾元素当基准,原地分区,递归切片——注意 arr[:pivot] 和 arr[pivot+1:] 是切片操作,不是新分配数组,性能关键就在这。
为什么不能直接传 []int 进去排序?
Go 切片是引用类型但底层数组指针不可变,函数内重赋值不会影响调用方。你必须原地改元素,而不是试图 arr = append(...) 或 arr = sort.Ints(...) 那种“换底层数组”的操作。
- 错误写法:
arr = append([]int{}, arr...)→ 新建底层数组,原切片没变 - 正确思路:所有交换、移动都发生在原
arr的元素索引上 - 递归调用时传子切片(如
arr[lo:hi])没问题,因为共享底层数组
partition 函数里 i 和 j 的初始值和边界容易错
常见崩溃或逻辑错:越界、漏比较、基准没归位。
-
j必须从0跑到pivot-1(不包含基准),否则会把基准自己也比一遍 -
i从0开始,代表“ - 如果用首元素当基准,得先把它挪到末尾再分区,否则
arr[0]被覆盖后找不回来 - 空切片或单元素切片必须提前
return,否则len(arr)-1会是负数
实际项目里别真手写 quicksort
Go 标准库 sort.Slice 底层就是优化过的 introsort(快排 + 堆排 + 插入排序混合),对小数组自动切到插入排序,深度过深自动切堆排防退化。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 手写版在随机数据下表现接近,但最坏 O(n²) 没防护
- 没有三数取中或随机选基准,遇到已排序数组会卡死
- 泛型支持要自己写类型参数,而
sort.Slice只要提供比较函数就行 - 真要调试排序行为,加日志比改手写逻辑快得多
sort.Slice。最常被忽略的点:手写时忘了切片是引用传递但长度/容量可变,arr[:n] 看似安全,但若原切片后面还有数据,partition 过程中可能意外读到脏值——务必确保传入的是干净子切片,或一开始就复制一份。
立即学习“go语言免费学习笔记(深入)”;

















