快排分区易错的核心在于边界处理混乱;应统一采用左闭右开区间,基准选arr[l],双指针从l+1和r-1开始,停右指针后交换arr[l]与arr[right]。

快排的分区逻辑为什么总写错?
核心卡点在 partition 函数里边界处理——很多人用 left 和 right 当闭区间,但选基准后移动指针时没统一规则,导致越界或死循环。
- 推荐固定用「左闭右开」区间:
quicksort(arr, l, r)表示排序arr[l]到arr[r-1],这样递归调用更干净(比如quicksort(arr, l, pivot)和quicksort(arr, pivot + 1, r)) - 基准值选
arr[l]最简单;双指针从l+1和r-1开始向中间扫,遇到 = 基准的停右指针,然后交换 - 最后把基准换到分界点:交换
arr[l]和arr[right](此时right是最后一个 - 别忘了递归前检查区间长度:
if (r - l ,否则小数组也递归,反而拖慢速度
std::sort 不能直接替代手写快排?
能,但不是所有场景都该用 std::sort。它底层是混合排序(introsort),平均快、最坏有保障,但无法控制分区策略或插入阈值。
- 需要稳定分区位置做调试/教学?必须手写
partition - 数据有大量重复值?标准库版本可能退化,你得手动加三路划分(
low/mid/high三段) - 嵌入式或内存受限环境?
std::sort可能隐式分配临时缓冲区,而手写可全程原地 - 想测不同 pivot 策略(中位数、随机)对性能影响?只能自己实现
随机 pivot 能防 O(n²) 但要注意什么?
不加随机化,快排在已排序或逆序数组上就是 O(n²),加了之后期望复杂度回到 O(n log n),但实现容易出错。
- 别在每次递归都调
rand()——要先srand(time(nullptr)),但只调一次;否则多线程下会崩,且重复 seed 导致重复序列 - 交换 pivot 到左端再分区:先生成
rand_idx∈ [l,r-1],再swap(arr[l], arr[rand_idx]) - 用
std::random_device+std::mt19937更靠谱,但注意 C++11 后才稳定支持;老项目慎用 - 如果数据量小(
r - l ),随机化收益几乎为零,不如直接用插入排序
递归太深导致栈溢出怎么压?
快排最坏递归深度 O(n),尤其单边倾斜时,std::stack 模拟或尾递归优化是常见解法,但 C++ 编译器不一定帮你优化尾递归。
立即学习“C++免费学习笔记(深入)”;
- 优先「先递归小半边,再循环处理大半边」:分区后比较左右段长度,对短的那段递归调用,长的那段用 while 循环重置参数继续处理
- 完全去递归?可用
std::stack<:pair int>></:pair>存待排区间,每次 pop 一个,分区后 push 两个新区间——但注意 push 顺序影响栈深度 - 别信「加个
inline就能消除递归」:编译器对递归函数基本不内联 - Linux 下默认栈大小约 8MB,若数组超千万级元素,哪怕平衡分割也可能爆栈,此时必须迭代化
实际写的时候,pivot 随机化、小数组切换插入排序、尾递归优化这三点叠加,才能让手写快排在真实数据上既稳又快。漏掉任意一个,都可能在某个数据分布下突然变慢一倍。











