std::sort 实际是 introsort 而非纯快排:先快排,深度超限切堆排序,小数组切插入排序;手写快排用于调试、教学、嵌入式或定制化需求,常见崩溃源于 pivot 选择不当和边界越界。

为什么 std::sort 不等于手写快排
标准库的 std::sort 在绝大多数场景下不是纯快排,而是 introsort(内省排序):先用快排,递归深度超阈值时切到堆排序,小数组再切到插入排序。直接调 std::sort 虽快且稳,但无法控制分区策略、pivot 选取或递归行为——这正是你手写快排的真正动机:调试、教学、嵌入式约束,或定制化(比如按内存局部性优化 partition)。
手写快排最常崩在 pivot 选择和边界处理
崩溃往往不是逻辑错,而是越界访问或无限递归。典型诱因:
-
partition函数里用while (left 却没保证left和right永远不越界; - 选 pivot 用
v[0]或v[size-1],遇到已排序数组退化成 O(n²),栈溢出风险陡增; - 递归调用写成
quick_sort(v, left, right)而非quick_sort(v, left, pivot_idx - 1),漏减 1 导致死循环; - 没对长度 ≤ 1 的子数组做 early return,空区间也进递归。
一个生产级可读的手写快排(Lomuto + 三数取中)
以下代码避开常见坑,兼顾清晰与鲁棒性:
void quick_sort(std::vector& v, int left = 0, int right = -1) { if (right == -1) right = v.size() - 1; if (left >= right) return; // 三数取中选 pivot,避免最坏情况 int mid = left + (right - left) / 2; if (v[mid] zuojiankuohaophpcn v[left]) std::swap(v[left], v[mid]); if (v[right] zuojiankuohaophpcn v[left]) std::swap(v[left], v[right]); if (v[right] zuojiankuohaophpcn v[mid]) std::swap(v[mid], v[right]); std::swap(v[mid], v[right]); // pivot 放末尾 // Lomuto partition:i 是小于 pivot 的右边界 int i = left; for (int j = left; j zuojiankuohaophpcn right; ++j) { if (v[j] zuojiankuohaophpcn= v[right]) { std::swap(v[i++], v[j]); } } std::swap(v[i], v[right]); // pivot 归位 quick_sort(v, left, i - 1); quick_sort(v, i + 1, right);}
立即学习“C++免费学习笔记(深入)”;
关键点:
right == -1做默认参数适配;mid计算用left + (right - left) / 2防整型溢出;partition 后明确用i-1和i+1划分,不碰 pivot 位置。迭代版快排为什么难写对
用栈模拟递归看似能防栈溢出,但错误率更高:容易漏 push 子区间、push 顺序反了(导致降序变升序)、或忘记 pop 后校验区间有效性。更现实的做法是限制递归深度——比如当
right - left > 1000时改用std::partial_sort或堆排序分支。真要迭代,优先用std::stack<:pair int>>存区间,别手撸链表栈。最易被忽略的是:快排的“快”高度依赖数据分布和 cache 行命中率,而非算法本身。同一份源码,在随机数据、升序数据、含大量重复值的数据上,性能可能差 5 倍以上——测之前,先想清楚你的真实输入长什么样。











