微信登录

快速排序 - 算法实现 - 快速排序的代码实现

快速排序 - 算法实现 - 快速排序的代码实现

引言

在计算机科学的众多算法中,排序算法是基础且至关重要的一部分。而快速排序(Quick Sort)以其平均情况下卓越的时间复杂度和高效的性能,在排序领域占据着重要地位。它由托尼·霍尔(Tony Hoare)在 1960 年提出,是一种分治算法,被广泛应用于各种实际场景中,如数据库排序、搜索引擎结果排序等。本文将深入探讨快速排序的原理,并给出其代码实现。

快速排序的原理

快速排序采用分治策略,将一个大问题分解为多个小问题来解决。其基本步骤如下:

  1. 选择基准元素(Pivot):从待排序数组中选择一个元素作为基准元素。基准元素的选择有多种方式,常见的有选择数组的第一个元素、最后一个元素或中间元素等。
  2. 分区操作(Partition):将数组中的元素重新排列,使得所有小于基准元素的元素位于基准元素的左边,所有大于基准元素的元素位于基准元素的右边。这个过程称为分区操作,分区操作完成后,基准元素就处于其最终排序的位置。
  3. 递归排序:对基准元素左右两边的子数组分别递归地应用快速排序算法,直到子数组的长度为 1 或 0,此时子数组已经有序。

分区操作的详细步骤

为了更好地理解分区操作,我们以选择数组的最后一个元素作为基准元素为例,详细说明分区操作的步骤:

  1. 初始化两个指针 iji 指向数组的起始位置,j 从起始位置开始遍历数组,直到倒数第二个元素。
  2. 遍历数组,如果当前元素小于基准元素,则将该元素与 i 指向的元素交换位置,并将 i 指针向后移动一位。
  3. 遍历结束后,将基准元素与 i 指向的元素交换位置,此时基准元素左边的元素都小于它,右边的元素都大于它。

快速排序的代码实现

Python 代码实现

  1. def quick_sort(arr):
  2. if len(arr) <= 1:
  3. return arr
  4. else:
  5. # 选择最后一个元素作为基准元素
  6. pivot = arr[-1]
  7. left = []
  8. right = []
  9. for i in range(len(arr) - 1):
  10. if arr[i] < pivot:
  11. left.append(arr[i])
  12. else:
  13. right.append(arr[i])
  14. return quick_sort(left) + [pivot] + quick_sort(right)
  15. # 测试代码
  16. arr = [3, 6, 8, 10, 1, 2, 1]
  17. sorted_arr = quick_sort(arr)
  18. print("排序后的数组:", sorted_arr)

Java 代码实现

  1. import java.util.Arrays;
  2. public class QuickSort {
  3. public static void quickSort(int[] arr, int low, int high) {
  4. if (low < high) {
  5. // 分区操作,获取基准元素的最终位置
  6. int pivotIndex = partition(arr, low, high);
  7. // 递归排序基准元素左边的子数组
  8. quickSort(arr, low, pivotIndex - 1);
  9. // 递归排序基准元素右边的子数组
  10. quickSort(arr, pivotIndex + 1, high);
  11. }
  12. }
  13. private static int partition(int[] arr, int low, int high) {
  14. // 选择最后一个元素作为基准元素
  15. int pivot = arr[high];
  16. int i = low - 1;
  17. for (int j = low; j < high; j++) {
  18. if (arr[j] < pivot) {
  19. i++;
  20. // 交换 arr[i] 和 arr[j]
  21. int temp = arr[i];
  22. arr[i] = arr[j];
  23. arr[j] = temp;
  24. }
  25. }
  26. // 交换 arr[i+1] 和 arr[high](基准元素)
  27. int temp = arr[i + 1];
  28. arr[i + 1] = arr[high];
  29. arr[high] = temp;
  30. return i + 1;
  31. }
  32. public static void main(String[] args) {
  33. int[] arr = {3, 6, 8, 10, 1, 2, 1};
  34. quickSort(arr, 0, arr.length - 1);
  35. System.out.println("排序后的数组: " + Arrays.toString(arr));
  36. }
  37. }

复杂度分析

复杂度类型 具体情况 复杂度
时间复杂度 平均情况 $O(n log n)$
时间复杂度 最坏情况 $O(n^2)$
空间复杂度 平均情况 $O(log n)$
空间复杂度 最坏情况 $O(n)$

平均情况

在平均情况下,快速排序的时间复杂度为 $O(n log n)$,这是因为每次分区操作都能将数组大致分成两个相等的子数组,递归深度为 $log n$,每次分区操作的时间复杂度为 $O(n)$。

最坏情况

最坏情况发生在选择的基准元素总是数组中的最大或最小元素时,此时每次分区操作只能将数组分成一个长度为 0 的子数组和一个长度为 $n-1$ 的子数组,递归深度为 $n$,时间复杂度为 $O(n^2)$。

空间复杂度

快速排序的空间复杂度主要取决于递归调用的栈深度。在平均情况下,递归深度为 $log n$,因此空间复杂度为 $O(log n)$;在最坏情况下,递归深度为 $n$,空间复杂度为 $O(n)$。

优化建议

为了避免最坏情况的发生,可以采用以下优化策略:

  1. 随机选择基准元素:随机选择一个元素作为基准元素,这样可以减少最坏情况发生的概率。
  2. 三数取中法:选择数组的第一个元素、中间元素和最后一个元素中的中位数作为基准元素,这样可以使基准元素更接近数组的中间值。

总结

快速排序是一种高效的排序算法,其平均时间复杂度为 $O(n log n)$,在实际应用中表现出色。通过本文的介绍,我们了解了快速排序的原理、代码实现和复杂度分析,并给出了一些优化建议。希望读者能够掌握快速排序算法,并在实际项目中灵活运用。