直接调用 std::sort 最稳妥,因其底层为 introsort,兼顾效率与稳定性;手写快排仅适用于算法题、教学或禁用 stl 场景,需重点处理 pivot 选择和 partition 边界。

为什么手写 std::sort 通常没必要
除非你在写算法题、教学代码,或者明确被要求禁用 STL,否则直接调 std::sort 是最稳妥的选择。它底层是 introsort(快排 + 堆排 + 插入排序混合),既避免快排最坏 O(n²) 的退化,又对小数组做优化,还支持自定义比较器。
手写快排的常见动机其实是:面试要写、OJ 禁用 STL、想理解 partition 过程。这时候得盯住两个核心:怎么选 pivot,怎么写 partition —— 错一步就可能栈溢出、死循环或不稳定。
partition 函数最容易写错的三种情况
手写快排的骨架是 partition,它把数组划成「≤pivot」和「>pivot」两部分。错误往往藏在边界处理里:
- 用
while (left 却在循环内没严格保证 <code>left和right不越界,导致访问arr[-1]或arr[n] - pivot 选了
arr[0]后,从左往右找第一个>= pivot,但从右往左却找—— 两边逻辑不对称,容易漏交换或卡住 - 递归调用时传错区间,比如写成
quick_sort(arr, left, right)而不是quick_sort(arr, left, index - 1),造成无限递归
推荐用「双指针同向扫描」写法,更直观:index 记录已处理中小于 pivot 的右边界,遍历中遇到小值就 swap 到 index 位置并 ++index。
立即学习“C++免费学习笔记(深入)”;
C++ 里递归快排的栈溢出风险怎么控
最坏情况下(如已排序数组 + 固定选首元素为 pivot),递归深度达 O(n),容易爆栈。生产环境几乎不用纯递归快排。
- 加个深度限制:当
right - left > 10才递归,否则切到std::insertion_sort(自己写三行就行) - 总是先递归处理较短的子区间,再用循环处理较长的 —— 这能把最坏栈空间压到 O(log n)
- 避免深拷贝:传
std::vector<int>&</int>或裸指针 + 长度,别传std::vector<int></int>
示例关键片段:
if (right - left < 10) { insertion_sort(arr + left, right - left + 1); return; }
手写时要不要管稳定性、重复元素、迭代实现
标准快排天生不稳定,且对大量重复元素(如全是 5)性能暴跌 —— 每次 partition 只能缩一点点。这不是 bug,是设计如此。
- 要稳定?换归并排序。快排改稳定要额外空间,得不偿失
- 对付重复元素,用三路 partition(
pivot),std::sort内部就用了类似思路 - 真要迭代版,得手动模拟栈存
[left, right]区间,但代码复杂度陡增,调试困难;只在嵌入式等极端栈受限场景才考虑
真正该花时间的地方,是测试边界:空数组、单元素、全相同、逆序、含 INT_MIN/INT_MAX —— 这些地方一跑就崩,比琢磨“最优 pivot”实在得多。








