1169 字
6 分钟

🚀 快速排序三种实现方式对比分析

快速排序三种实现方式对比分析#

说明:以下三种方法均基于 C++ 实现,适用于标准快速排序场景。所有代码均可在本地编译运行,并在 Typora 中完美渲染。

来源题目/题单说明
牛客https://www.nowcoder.com/practice/3385982ae71d4a1ca8bf3d03614c0325快速排序
牛客https://www.nowcoder.com/practice/34731183c31e4276b2d081590b2f3a4b排序(快速排序)
洛谷https://www.luogu.com.cn/problem/T588555T588555 快速排序
洛谷https://www.luogu.com.cn/problem/U223702U223702 快速排序
洛谷https://www.luogu.com.cn/problem/T292012T292012 【模板】快速排序
洛谷https://www.luogu.com.cn/problem/T588555T588555 快速排序
洛谷https://www.luogu.com.cn/problem/T318859T318859 Quick Sort快排

一、总体概览#

特性quickSort1quickSort2quickSort3
分区方式基于值的单向扫描 + 随机 pivot双向指针(Hoare 分区)三路快排(Dijkstra 三分法)
Pivot 选择随机选取元素中间位置元素中间位置元素
重复元素优化
平均时间复杂度O(nlogn)O(n \log n)O(nlogn)O(n \log n)O(nlogn)O(n \log n),大量重复时接近 O(n)O(n)
是否推荐⚠️ 谨慎✅ 推荐✅(修正后)

二、方法一:quickSort1 —— 基于值的随机 Partition(Lomuto 变种)#

✅ 完整可运行代码#

#include <cmath>
#include <iostream>
using namespace std;
const int N = 1e5 + 5;
int nums[N];
// Partition 函数:将 <= x 的元素移到左边,并确保一个 x 落在最终位置
int Partition(int l, int r, int x) {
int a = l, xi = l;
for (int i = l; i <= r; i++) {
if (nums[i] <= x) {
swap(nums[a], nums[i]);
if (nums[a] == x) { xi = a; }
a++;
}
}
swap(nums[xi], nums[a - 1]); // 将一个 x 放到正确位置
return a - 1;
}
void quickSort1(int l, int r) {
if (l >= r) return;
// 随机选择一个下标,取其值作为 pivot
int rand_index = l + rand() % (r - l + 1);
int x = nums[rand_index];
int p = Partition(l, r, x);
quickSort1(l, p - 1);
quickSort1(p + 1, r);
}
int main() {
srand(time(0)); // 初始化随机种子
int n;
scanf("%d", &n);
for (int i = 0; i < n; i++) { scanf("%d", &nums[i]); }
quickSort1(0, n - 1);
for (int i = 0; i < n; i++) { printf("%d ", nums[i]); }
return 0;
}

⚠️ 注意事项#

  • 虽然引入了随机化,但对重复元素处理不完善
  • 在极端重复数据下可能性能退化或行为异常。
  • 不建议用于生产环境。

三、方法二:quickSort2 —— 经典 Hoare 分区(双向指针)#

✅ 完整可运行代码#

#include <cstdio>
#include <iostream>
using namespace std;
const int N = 1e5 + 5;
int nums[N];
void quickSort2(int l, int r) {
if (l >= r) return;
int i = l - 1, j = r + 1;
// int x = nums[l + ((r - l) >> 1)]; // 取中间元素为 pivot
int x = nums[l + rand() % (r - l + 1)]; // 随机选择一个下标 为 pivot (推荐)
while (i < j) {
do { i++; } while (nums[i] < x);
do { j--; } while (nums[j] > x);
if (i < j) { swap(nums[i], nums[j]); }
}
// 注意:Hoare 分区返回的是 j,递归 [l, j] 和 [j+1, r]
quickSort2(l, j);
quickSort2(j + 1, r);
}
int main() {
int n;
scanf("%d", &n);
for (int i = 0; i < n; i++) { scanf("%d", &nums[i]); }
quickSort2(0, n - 1);
for (int i = 0; i < n; i++) { printf("%d ", nums[i]); }
return 0;
}

✅ 优点#

  • 代码简洁,效率高。
  • 中间选 pivot 对有序/逆序数组表现良好。
  • 是经典且广泛使用的快排实现。

四、方法三:quickSort3 —— 三路快排(Three-way QuickSort)#

重要:原始代码存在严重越界 Bug,以下为修正后的正确版本

✅ 完整可运行代码#

#include <cstdio>
#include <iostream>
using namespace std;
const int N = 1e5 + 5;
int nums[N];
void quickSort3(int l, int r) {
if (l >= r) return;
// int x = nums[l + ((r - l) >> 1)]; // pivot 值
int x = nums[l + rand() % (r - l + 1)]; // 随机生成位置取 pivot 值(推荐)
int lt = l; // [l, lt-1] 为 < x 的区域
int gt = r; // [gt+1, r] 为 > x 的区域
int i = l; // 当前扫描指针
while (i <= gt) {
if (nums[i] < x) {
swap(nums[i], nums[lt]);
lt++;
i++;
} else if (nums[i] > x) {
swap(nums[i], nums[gt]);
gt--;
// 注意:此处 i 不自增,因为从 gt 换来的元素未被检查
} else {
i++; // 等于 x,直接跳过
}
}
// 递归处理 < x 和 > x 的部分,== x 的部分已就位
quickSort3(l, lt - 1);
quickSort3(gt + 1, r);
}
int main() {
int n;
scanf("%d", &n);
for (int i = 0; i < n; i++) { scanf("%d", &nums[i]); }
quickSort3(0, n - 1);
for (int i = 0; i < n; i++) { printf("%d ", nums[i]); }
return 0;
}

✅ 优势场景#

  • 输入包含大量重复元素(如 [5,5,5,1,5,9,5])。
  • 时间复杂度可接近 O(n)O(n)
  • 是处理“重复密集型”数据的最佳快排变种。

五、总结与使用建议#

方法适用场景是否推荐
quickSort1教学演示(展示随机化思想)❌ 不推荐(有缺陷)
quickSort2通用排序、竞赛、一般应用✅ 强烈推荐
quickSort3含大量重复元素的数据✅ 推荐

💡 最佳实践

  • 日常使用 → quickSort2
  • 已知数据重复率高 → quickSort3(修正版)
  • 生产环境 → 直接使用 std::sort(基于 Introsort,更健壮)

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或赞助支持!

赞助
🚀 快速排序三种实现方式对比分析
https://sunyzhi55.github.io/AstroFireflyBlog/posts/algorithm/quick-sort/
作者
Summer Flame 🔥
发布于
2025-11-28
许可协议
CC BY-NC-SA 4.0
最后更新于 2025-11-28,距今已过 34 天

部分内容可能已过时

目录