sort.Slice 是首选,因其经深度优化可防退化、支持 nil 安全与任意字段,而手写快排易因空切片、边界错误等导致 panic 或逻辑错误。

别手写快排——sort.Slice 覆盖 99% 场景,安全、快、防退化;手写版本极易因空切片、边界错误、pivot 归位失败导致 panic 或逻辑错乱。
为什么 sort.Slice 是首选,而不是自己实现
标准库的 sort.Slice 已针对 Go 运行时深度优化:自动切换到堆排序防最坏 O(n²)、递归深度有硬限、底层用 unsafe 批量比较、支持任意字段和 nil 安全判断。你写的“简洁版”快排在真实数据(如已部分有序、含大量重复值、空切片)上大概率触发 panic 或排序不全。
-
sort.Slice要求传入非 nil 切片,且less函数必须满足严格弱序:比如less(i, i)必须返回false,写成return a[i].X 就会崩溃 - 字段可能为 nil?
less里必须显式判空:if a[i].Name == nil { return false },否则 runtime panic - 想按绝对值排序?
sort.Slice(nums, func(i, j int) bool { return abs(nums[i]) —— 一行解决,无需改算法
partition 函数里为什么不能用 len(arr) - 1 当索引
空切片时 len(arr) 是 0,len(arr) - 1 就是 -1,直接当索引用会 panic。更隐蔽的是:递归中传入的子切片(如 arr[lo:hi])长度远小于原始切片,但右边界是 hi - 1,不是 len(arr) - 1。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 错误写法:
pivot := arr[len(arr)-1]—— 没检查len(arr) == 0,一遇到空输入就崩 - 正确做法:函数签名带
low, high int参数,pivot 取arr[high];或先判if len(sub) == 0 { return }再取末尾 - 另一个坑:
for j := 0; j 在 <code>len(arr) == 1时循环不执行,pivot 没参与分区,最终位置错乱
非要手写 quickSort,只保留这个最小可靠骨架
仅限算法题、教学、或嵌入式禁用标准库场景。以下版本已验证:空切片安全、pivot 归位正确、不新建底层数组、终止条件鲁棒。
立即学习“go语言免费学习笔记(深入)”;
func quickSort(nums []int, low, high int) {
if low < high {
pi := partition(nums, low, high)
quickSort(nums, low, pi-1)
quickSort(nums, pi+1, high)
}
}
<p>func partition(nums []int, low, high int) int {
pivot := nums[high]
i := low - 1
for j := low; j < high; j++ {
if nums[j] <= pivot {
i++
nums[i], nums[j] = nums[j], nums[i]
}
}
nums[i+1], nums[high] = nums[high], nums[i+1]
return i + 1
}- 递归终止条件必须是
if low ,不是 <code>if len(nums) —— 后者在子调用中完全失效 - 所有交换都用
nums[i], nums[j] = nums[j], nums[i]原地操作,绝不用append拼新切片;否则原变量不变、GC 压力大、空间复杂度爆到 O(n²) - 选
nums[high]当 pivot 是因边界清晰,不是因为它“最优”;选中间或随机需额外开销,对业务代码属于过早优化
真正容易被忽略的点:手写快排不是为了“更快”,而是为了控制分区逻辑(比如按结构体某字段绝对值、多级优先级、或配合其他约束条件)。一旦需求超出 sort.Slice 表达能力,再考虑定制 partition —— 但请先确认它真不可替代。

















