C++ 怎么实现快速排序 C++ 分治算法递归逻辑详解【考研】

5次阅读

应优先递归处理较小的子数组并显式循环处理较大的子数组,同时对长度≤10的子数组改用插入排序,可将最坏递归深度从O(n)降至O(log n)。

C++ 怎么实现快速排序 C++ 分治算法递归逻辑详解【考研】

快速排序的递归结构怎么写才不溢出

递归实现快排时,最常踩的坑是没控制好递归深度,尤其在输入已接近有序时,partition 每次只减少一个元素,导致 O(n) 层递归,容易触发溢出(尤其考研机试环境栈空间小)。必须用「尾递归优化」或「小数组转插入排序」来缓解。

实操建议:

  • 对子数组长度 right - left + 1 的情况,直接调用 insertion_sort,避免浅层递归开销
  • 递归调用前,先处理较短的子区间,再用循环处理较长的——这能将最坏栈深度压到 O(log n)
  • 不要写成 quick_sort(left, pivot-1); quick_sort(pivot+1, right); 这种裸递归,它不保证栈深度

partition 函数选哪个版本更稳妥(Lomuto vs Hoare)

考研代码题里,Hoare 分区虽然交换次数少、边界处理紧凑,但初学者极易写错指针越界(比如 ++i 后没判 i 就访问 arr[i]);Lomuto 更直观,但对全相同元素退化严重(每次只缩一格)。

推荐用改良版 Lomuto,加随机化 pivot:

立即学习C++免费学习笔记(深入)”;

int partition(vector& arr, int left, int right) {     int idx = left + rand() % (right - left + 1);     swap(arr[idx], arr[right]); // 随机选 pivot 并换到末尾     int pivot = arr[right];     int i = left;     for (int j = left; j < right; ++j) {         if (arr[j] <= pivot) {             swap(arr[i++], arr[j]);         }     }     swap(arr[i], arr[right]);     return i; }

注意:rand() 要提前调 srand(time(0)),但机试中若禁止 time(),可用 std::random_device 或固定种子(如 srand(42)

如何让快排稳定(考研偶尔考“稳定快排”变种)

标准快排本质不稳定——因为 partition 中相等元素可能跨距交换。真要稳定,不能靠改比较逻辑,得换策略:

  • 放弃原地排序,用额外空间:把小于、等于、大于 pivot 的元素分别存入三个 vector,再合并回原数组
  • 改用「三路快排」(Dutch National Flag):返回 [lt, gt] 区间,使 arr[left..lt-1] ,arr[lt..gt] == pivotarr[gt+1..right] > pivot;这样相等元素不会乱序移动
  • 考研若明确要求“稳定”,大概率是陷阱题——快排无法在 O(1) 空间 + O(n log n) 时间下稳定,应立刻质疑题干,或转向归并

STL sort 是不是快排?能不能直接用在考研机试

std::sort 在大多数 STL 实现中是「 introsort 」——快排 + 堆排序兜底 + 插入排序收尾,平均 O(n log n),最坏也 O(n log n)。但它内部逻辑黑盒,考试中若题目明确要求“手写快排”,用 std::sort 会判零分。

另外要注意:

  • 某些 OJ 禁用 (如部分高校自建平台),编译直接报错
  • std::sort 默认升序,降序需传 greater(),别漏写括号
  • 如果数组是 vector,记得传 v.begin(), v.end(),不是 v, v + n

真正难的不是写出快排,而是想清楚 pivot 怎么选、分区边界怎么控、递归怎么剪、以及什么时候该放弃快排去用归并——这些才是考研现场卡住人的地方。

text=ZqhQzanResources