递归终止条件必须是 if left >= right,而非 if left >= len(arr);因arr始终为原始切片,子区间由left、right界定,left >= right表示0或1个元素时安全退出,避免越界panic。

递归终止条件必须写成 if left >= right,不是 if left >= len(arr)
这是手写快排最常崩的第一步。用 len(arr) 是典型错误——arr 始终是原始切片,而你的递归操作只在子区间 [left, right] 内进行。right 可能远小于 len(arr) - 1,一旦写成 left >= len(arr),第一次调用就成立(left=0,len(arr)>0),整个排序直接跳过,返回原数组。
正确逻辑是:子区间只剩 0 或 1 个元素时停止。即 left == right(1 个)或 left > right(0 个),合并为 left >= right。
- 空切片时
len(arr) == 0,若误用arr[len(arr)-1]会 panic;但left >= right在left=0, right=-1时自然成立,安全退出 - 递归传入
quickSort(arr, low, pivotIndex-1)后,pivotIndex-1可能小于low,此时low > pivotIndex-1→left > right,应立即返回 - 别依赖
len(sub) == 0判空:Go 允许arr[3:2]返回空切片,但下标本身已越界;边界判断必须靠left和right的数值关系
partition 函数里 pivot 下标不能硬写 len(arr) - 1
很多实现一上来就写 pivot := arr[len(arr)-1],这在子递归中极危险。因为传入的 arr 是原始切片,len(arr) 不随子区间变化,而你实际要取的是当前区间的右端点值,即 arr[right]。
更糟的是,空区间(left > right)还没进 partition 就该退出;但如果没守好终止条件,len(arr) == 0 时 len(arr)-1 就是 -1,直接 panic。
立即学习“go语言免费学习笔记(深入)”;
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 正确做法:函数签名带
left, right int,pivot 显式取arr[right]或arr[left],并确保调用前已满足left - 如果想随机选 pivot,应在
[left, right]范围内生成索引:i := left + rand.Intn(right-left+1),再交换arr[i], arr[right] - 别用
arr[:mid]和arr[mid:]传参——虽然不复制数据,但底层数组可能极大,拖慢 GC,且容易让栈帧意外延长
为什么用 append 拼左右子数组永远排不对原切片
常见错误写法:left = append([]int{}, ...); right = append([]int{}, ...); return append(append(left, pivot), right...)。这不是算法错,是 Go 切片语义误解。
append 在底层数组容量不足时会分配新内存,返回的新切片指向新地址,原切片变量完全没变。你看到的“结果”只是临时副本,原数组未被修改,后续 sort.Search 查不到,GC 还压力大。
- 真正有效的快排必须原地交换:
arr[i], arr[j] = arr[j], arr[i],所有递归都操作同一底层数组 - 分区后递归调用也要传相同底层数组:
quickSort(arr, left, pivotIndex-1),而不是传arr[left:pivotIndex] - 如果你非要返回新切片,那就别叫“排序”,那是“生成有序副本”,空间复杂度从 O(1) 暴涨到 O(n²),且无法替代原地排序场景
真实项目里,别手写快排——除非你清楚自己在绕过什么
sort.Slice 覆盖 99% 场景:它用的是 pdqsort(混合快排/堆排/插入排序),防退化、安全、快。手写版本极易因边界、空切片、pivot 归位错误导致 panic 或逻辑错乱。
真要手写,只保留最小可靠骨架:带 left, right 参数、终止条件 left >= right、pivot 取 arr[right]、原地 swap、小数组(如 right - left )改用插入排序。其他都是给算法题或教学准备的。
最容易被忽略的点:递归深度本身不是问题,但每层若还误传大底层数组、频繁新建切片头,栈帧会迅速膨胀;Go 默认 goroutine 栈仅 2KB,千万级数据下,错一点就栈溢出——这不是加 runtime/debug.SetMaxStack 能解决的。

















