快速排序算法 (附 Python/Java/C/C++ 程序)

2025 年 5 月 7 日 | 阅读 12 分钟

快速排序是一种利用分治技术的排序算法。它选择一个基准元素,并将其放在已排序数组的适当位置。

分治法是一种将算法分解为子问题,然后解决子问题,并将结果组合起来以解决原始问题的方法。

  • 分治:首先,选择一个基准元素。然后,将数组分区或重新排列成两个子数组,使得左子数组中的每个元素都小于或等于基准元素,右子数组中的每个元素都大于基准元素。
  • 解决:使用快速排序递归地对两个子数组进行排序。
  • 合并:合并已排序的数组。

步骤:

步骤 1:选择基准元素。通常选择第一个、最后一个或中位数元素作为基准元素。

步骤 2:对数组进行分区。为此,请围绕基准元素重新排列数组的元素。分区后,基准元素左侧的元素(左子数组)小于基准元素,基准元素右侧的元素(右子数组)大于基准元素。

步骤 3:对左子数组和右子数组重复上述步骤。这可以通过递归完成。当子数组只剩下一个元素时,递归停止。因为单个元素已经是排序好的。

算法

快速排序算法的工作原理

实现快速排序的简单步骤如下。

步骤 1:选择基准元素。通常选择第一个、最后一个或中位数元素作为基准元素。在本例中,我们选择最后一个元素作为基准。此外,取两个指针 j 和 k,其中 j 的值为 -1,k 指向数组的第 0 个索引。移动 j 指针,使得 j 指针指向的元素及其左侧的元素都小于基准元素。

Quick Sort Algorithm

步骤 2:将 a[k],即 19,与基准元素进行比较。我们看到 a[k] > 基准元素。因此,将 k 增加 1。现在,k 指向第 1 个索引。

Quick Sort Algorithm

步骤 3:将 a[k],即 4,与基准元素进行比较。我们看到 a[k] < 基准元素。因此,将 j 增加 1。现在,j 指向第 0 个索引。

Quick Sort Algorithm

步骤 4:交换 a[j] 和 a[k],并将 k 增加 1。现在,k 指向第 2 个索引。

Quick Sort Algorithm

步骤 5:将 a[k],即 13,与基准元素进行比较。我们看到 a[k] < 基准元素。因此,将 j 增加 1。现在,j 指向第 1 个索引。

Quick Sort Algorithm

步骤 6:交换 a[j] 和 a[k],并将 k 增加 1。现在,k 指向第 3 个索引。

Quick Sort Algorithm

步骤 7:将 a[k],即 18,与基准元素进行比较。我们看到 a[k] > 基准元素。因此,将 k 增加 1。现在,k 指向第 4 个索引。

Quick Sort Algorithm

步骤 8:将 a[k],即 10,与基准元素进行比较。我们看到 a[k] < 基准元素。因此,将 j 增加 1。现在,j 指向第 2 个索引。

Quick Sort Algorithm

步骤 9:交换 a[j] 和 a[k],并将 k 增加 1。现在,k 指向第 5 个索引。

Quick Sort Algorithm

步骤 10:将 a[k],即 10,与基准元素进行比较。我们看到 a[k] < 基准元素。因此,将 j 增加 1。现在,j 指向第 3 个索引。

Quick Sort Algorithm

步骤 11:交换 a[j] 和 a[k],并将 k 增加 1。现在,k 指向第 6 个索引。

Quick Sort Algorithm

步骤 12:将 a[k],即 15,与基准元素进行比较。我们看到 a[k] < 基准元素。因此,将 j 增加 1。现在,j 指向第 4 个索引。

Quick Sort Algorithm

步骤 13:交换 a[j] 和 a[k]。由于 k 指向最后一个元素,因此进一步的检查和交换停止在此处。请注意,j 索引左侧的所有元素以及 j 索引处的元素都小于基准元素。

Quick Sort Algorithm

步骤 14:将 a[j + 1] 与基准元素交换(交换 18 和 17)。

Quick Sort Algorithm

我们看到基准元素已放置在排序数组的正确位置。

步骤 15:根据基准元素将数组划分为左子数组和右子数组。位于基准元素左侧的元素是左子数组的一部分,位于基准元素右侧的元素是右子数组的一部分。

Quick Sort Algorithm

步骤 16:对左子数组和右子数组重复所有步骤。

Quick Sort Algorithm

Python/Java/C/C++/C# 中的快速排序实现

Python 程序

立即执行

Java 程序

编译并运行

C++ 程序

编译并运行

C 语言程序

编译并运行

C# 程序

编译并运行

复杂度分析

场景时间复杂度
最佳情况(Ω(n log n):最佳情况发生在基准元素将数组分成两半时。
平均情况(θ(n log n)):平均而言,基准元素将数组分成两部分,但不一定是相等的部分。
最坏情况(O(n²)) 发生的情况是,当选择最大元素或最小元素作为已排序数组的基准元素时。

空间复杂度:空间复杂度为 **O(n)**,因为递归调用堆栈,其中 n 是输入数组中元素的总数。

快速排序的应用

  • 许多政府和私营部门实体使用商业计算来组织不同类型的数据,例如按名称/日期/价格排列文件、按学号对学生进行排序以及按指定 ID 组织账户配置文件,因为它是处理大型数据集最快的通用算法。
  • 在不需要稳定排序的地方,可以使用快速排序。要实现稳定性,可以使用归并排序。
  • 在编程语言的库函数中,快速排序用于对数组进行排序。例如,Java 中的 Arrays.sort()、C 中的 qsort 和 C++ 中的 sort 使用混合排序,其中快速排序是主要的排序算法。
  • 快速排序的不同版本用于识别 K 个最大或最小的元素。

快速排序的优点

  • 快速排序使用分治算法,这使得解决问题更加容易。
  • 快速排序在大型数据集上效果很好。
  • 快速排序的开销很低。这是因为它只需要少量的内存即可工作。
  • 快速排序对缓存友好。这是因为排序工作是在同一数组上完成的。它不会从任何辅助数组复制数据。
  • 对于稳定性不是问题的的大型数据集,快速排序是最快的通用算法。
  • 快速排序使用尾部递归。因此,可以进行所有尾部调用优化。
  • 可以使用快速排序进行并行处理,因为两个子数组可以独立处理。

快速排序的缺点

  • 快速排序的时间复杂度为 O(n²),当基准选择不当时会出现这种情况。
  • 快速排序不适合小型数据集。
  • 快速排序不是稳定排序。这意味着,(快速排序的结果)两个键相同的元素的相对顺序不会被保留。观察代码中交换元素时未考虑其原始位置。

结论

由于其速度和简单性,快速排序是最广泛使用和最高效的排序算法之一。其原地排序能力与分治方法的结合,使其适用于内存受限的系统和大型数据集。


下一主题归并排序