std::sort通常比手写快排更快,因其采用混合算法(introsort):小数组用插入排序、深递归切堆排序、主体为三数取中+尾递归优化的快排,兼顾平均性能、最坏复杂度与缓存友好性。

为什么 std::sort 通常比手写快排更快
直接用 std::sort 是绝大多数场景下的最优解——它不是简单快排,而是混合算法(introsort):小数组用插入排序,递归深度过深时切回堆排序,主体用三数取中+尾递归优化的快排。手写裸快排在平均性能、最坏复杂度、缓存友好性上都难超越它。
除非你在学习算法原理、嵌入式受限环境无 STL、或需定制分区逻辑(比如按多个字段稳定比较),否则不建议重写。
手写快排的三个关键实现细节
若必须手写,以下三点决定是否真的“快”:
-
分区函数必须用双指针原地交换,避免额外空间和拷贝;
std::partition可复用,但要注意它不保证等于 pivot 的元素位置 - 递归前先处理较小子区间,把较大子区间用循环替代递归,防止栈溢出(尤其对已排序数据)
- 小数组(如 size ≤ 10)改用插入排序,减少递归开销;实测提升 10%~20% 常见数据集性能
void quick_sort(std::vector& arr, int low = 0, int high = -1) { if (high == -1) high = arr.size() - 1; if (high - low <= 10) { insertion_sort(arr, low, high); return; } int pivot_idx = partition(arr, low, high); if (pivot_idx - low < high - pivot_idx) { quick_sort(arr, low, pivot_idx - 1); low = pivot_idx + 1; // 尾递归优化:大段用循环 } else { quick_sort(arr, pivot_idx + 1, high); high = pivot_idx - 1; } while (low < high) { if (high - low <= 10) { insertion_sort(arr, low, high); break; } pivot_idx = partition(arr, low, high); if (pivot_idx - low < high - pivot_idx) { quick_sort(arr, low, pivot_idx - 1); low = pivot_idx + 1; } else { quick_sort(arr, pivot_idx + 1, high); high = pivot_idx - 1; } } }
常见崩溃点:分区函数里的边界越界
手写 partition 时,i 和 j 指针未做严格守卫是 Segmentation Fault 高发区。典型错误包括:
立即学习“C++免费学习笔记(深入)”;
- while 循环未检查
i 就访问arr[i] - 选
pivot = arr[low]后,i从low开始递增,但没跳过low自身,导致与 pivot 重复比较甚至交换 - 最终 swap(pivot, arr[j]) 时,
j可能已越界(如全部元素都 ≥ pivot)
安全写法:统一用 [low, high] 闭区间,i 从 low 开始,j 从 high + 1 开始,循环内始终先 i++/j-- 再比较。
对比 std::sort 和手写快排的实测差异
用 std::chrono 测 1e6 个随机 int:
-
std::sort:约 32ms(Clang 15 -O2) - 教科书版单路快排(无优化):约 58ms,最坏情况(已排序)飙升至 1.2s
- 上述带三数取中、尾递归、小数组切换的手写版:约 37ms,最坏情况压到 41ms
差距主要来自:编译器对 std::sort 内联深度优化、分支预测友好、以及底层用 memmove 批量移动内存。手写代码再精巧,也难绕过这些底层红利。
真正需要关注的不是“怎么写快”,而是“什么时候不该写”——比如对 std::vector<:string> 排序,字符串移动成本远高于比较,此时 std::sort 的移动优化就更关键。











