快速排序是由东尼 · 霍尔所发展的一种排序算法。

快速排序是由东尼 · 霍尔所发展的一种排序算法。在平均状况下,排序 n 个项目要 Ο(nlogn) 次比较。在最坏状况下则需要 Ο(n2) 次比较,但这种状况并不常见。事实上,快速排序通常明显比其他 Ο(nlogn) 算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来。

快速排序使用分治法(Divide and conquer)策略来把一个串行(list)分为两个子串行(sub-lists)。

快速排序又是一种分而治之思想在排序算法上的典型应用。本质上来看,快速排序应该算是在冒泡排序基础上的递归分治法。

快速排序的名字起的是简单粗暴,因为一听到这个名字你就知道它存在的意义,就是快,而且效率高!它是处理大数据最快的排序算法之一了。虽然 Worst Case 的时间复杂度达到了 O(n²),但是人家就是优秀,在大多数情况下都比平均时间复杂度为 O(n logn) 的排序算法表现要更好,可是这是为什么呢,我也不知道。好在我的强迫症又犯了,查了 N 多资料终于在《算法艺术与信息学竞赛》上找到了满意的答案:

快速排序的最坏运行情况是 O(n²),比如说顺序数列的快排。但它的平摊期望时间是 O(nlogn),且 O(nlogn) 记号中隐含的常数因子很小,比复杂度稳定等于 O(nlogn) 的归并排序要小很多。所以,对绝大多数顺序性较弱的随机数列而言,快速排序总是优于归并排序。

1. 算法步骤

  1. 从数列中挑出一个元素,称为 “基准”(pivot);
  2. 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
  3. 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序;

2. 动图演示

快速排序 - 图1


代码实现

JavaScript

  1. function quickSort(arr, left, right) {
  2. var len = arr.length,
  3. partitionIndex,
  4. left = typeof left != 'number' ? 0 : left,
  5. right = typeof right != 'number' ? len - 1 : right;
  6. if (left < right) {
  7. partitionIndex = partition(arr, left, right);
  8. quickSort(arr, left, partitionIndex-1);
  9. quickSort(arr, partitionIndex+1, right);
  10. }
  11. return arr;
  12. }
  13. function partition(arr, left ,right) { // 分区操作
  14. var pivot = left, // 设定基准值(pivot)
  15. index = pivot + 1;
  16. for (var i = index; i <= right; i++) {
  17. if (arr[i] < arr[pivot]) {
  18. swap(arr, i, index);
  19. index++;
  20. }
  21. }
  22. swap(arr, pivot, index - 1);
  23. return index-1;
  24. }
  25. function swap(arr, i, j) {
  26. var temp = arr[i];
  27. arr[i] = arr[j];
  28. arr[j] = temp;
  29. }
  30. function partition2(arr, low, high) {
  31. let pivot = arr[low];
  32. while (low < high) {
  33. while (low < high && arr[high] > pivot) {
  34. --high;
  35. }
  36. arr[low] = arr[high];
  37. while (low < high && arr[low] <= pivot) {
  38. ++low;
  39. }
  40. arr[high] = arr[low];
  41. }
  42. arr[low] = pivot;
  43. return low;
  44. }
  45. function quickSort2(arr, low, high) {
  46. if (low < high) {
  47. let pivot = partition2(arr, low, high);
  48. quickSort2(arr, low, pivot - 1);
  49. quickSort2(arr, pivot + 1, high);
  50. }
  51. return arr;
  52. }

Python

  1. def quickSort(arr, left=None, right=None):
  2. left = 0 if not isinstance(left,(int, float)) else left
  3. right = len(arr)-1 if not isinstance(right,(int, float)) else right
  4. if left < right:
  5. partitionIndex = partition(arr, left, right)
  6. quickSort(arr, left, partitionIndex-1)
  7. quickSort(arr, partitionIndex+1, right)
  8. return arr
  9. def partition(arr, left, right):
  10. pivot = left
  11. index = pivot+1
  12. i = index
  13. while i <= right:
  14. if arr[i] < arr[pivot]:
  15. swap(arr, i, index)
  16. index+=1
  17. i+=1
  18. swap(arr,pivot,index-1)
  19. return index-1
  20. def swap(arr, i, j):
  21. arr[i], arr[j] = arr[j], arr[i]

Go

  1. func quickSort(arr []int) []int {
  2. return _quickSort(arr, 0, len(arr)-1)
  3. }
  4. func _quickSort(arr []int, left, right int) []int {
  5. if left < right {
  6. partitionIndex := partition(arr, left, right)
  7. _quickSort(arr, left, partitionIndex-1)
  8. _quickSort(arr, partitionIndex+1, right)
  9. }
  10. return arr
  11. }
  12. func partition(arr []int, left, right int) int {
  13. pivot := left
  14. index := pivot + 1
  15. for i := index; i <= right; i++ {
  16. if arr[i] < arr[pivot] {
  17. swap(arr, i, index)
  18. index += 1
  19. }
  20. }
  21. swap(arr, pivot, index-1)
  22. return index - 1
  23. }
  24. func swap(arr []int, i, j int) {
  25. arr[i], arr[j] = arr[j], arr[i]
  26. }

C++

  1. // 严蔚敏《数据结构》标准分割函数
  2. Paritition1(int A[], int low, int high) {
  3. int pivot = A[low];
  4. while (low < high) {
  5. while (low < high && A[high] >= pivot) {
  6. --high;
  7. }
  8. A[low] = A[high];
  9. while (low < high && A[low] <= pivot) {
  10. ++low;
  11. }
  12. A[high] = A[low];
  13. }
  14. A[low] = pivot;
  15. return low;
  16. }
  17. void QuickSort(int A[], int low, int high) // 快排母函数
  18. {
  19. if (low < high) {
  20. int pivot = Paritition1(A, low, high);
  21. QuickSort(A, low, pivot - 1);
  22. QuickSort(A, pivot + 1, high);
  23. }
  24. }

Java

  1. public class QuickSort implements IArraySort {
  2. @Override
  3. public int[] sort(int[] sourceArray) throws Exception {
  4. // 对 arr 进行拷贝,不改变参数内容
  5. int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);
  6. return quickSort(arr, 0, arr.length - 1);
  7. }
  8. private int[] quickSort(int[] arr, int left, int right) {
  9. if (left < right) {
  10. int partitionIndex = partition(arr, left, right);
  11. quickSort(arr, left, partitionIndex - 1);
  12. quickSort(arr, partitionIndex + 1, right);
  13. }
  14. return arr;
  15. }
  16. private int partition(int[] arr, int left, int right) {
  17. // 设定基准值(pivot)
  18. int pivot = left;
  19. int index = pivot + 1;
  20. for (int i = index; i <= right; i++) {
  21. if (arr[i] < arr[pivot]) {
  22. swap(arr, i, index);
  23. index++;
  24. }
  25. }
  26. swap(arr, pivot, index - 1);
  27. return index - 1;
  28. }
  29. private void swap(int[] arr, int i, int j) {
  30. int temp = arr[i];
  31. arr[i] = arr[j];
  32. arr[j] = temp;
  33. }
  34. }

PHP

  1. function quickSort(arr) <= 1)
  2. return $arr;
  3. $middle = $arr[0];
  4. $leftArray = array();
  5. $rightArray = array();
  6. for ($i = 1; arr); arr[$i] > $middle)
  7. $rightArray[] = i];
  8. else
  9. $leftArray[] = i];
  10. }
  11. leftArray);
  12. $leftArray[] = $middle;
  13. rightArray);
  14. return array_merge($leftArray, $rightArray);
  15. }

C

  1. typedef struct _Range {
  2. int start, end;
  3. } Range;
  4. Range new_Range(int s, int e) {
  5. Range r;
  6. r.start = s;
  7. r.end = e;
  8. return r;
  9. }
  10. void swap(int x, int y) {
  11. int t = *x;
  12. x = y;
  13. *y = t;
  14. }
  15. void quick_sort(int arr[], const int len) {
  16. if (len <= 0)
  17. return; // 避免 len 等於負值時引發段錯誤(Segment Fault)
  18. // r[] 模擬列表, p 為數量, r[p++] 為 push,r[--p] 為 pop 且取得元素
  19. Range r[len];
  20. int p = 0;
  21. r[p++] = new_Range(0, len - 1);
  22. while (p) {
  23. Range range = r[--p];
  24. if (range.start >= range.end)
  25. continue;
  26. int mid = arr[(range.start + range.end) / 2]; // 選取中間點為基準點
  27. int left = range.start, right = range.end;
  28. do {
  29. while (arr[left] < mid) left; // 檢測基準點左側是否符合要求
  30. while (arr[right] > mid) --right; // 檢測基準點右側是否符合要求
  31. if (left <= right) {
  32. swap(&arr[left], &arr[right]);
  33. left;
  34. right--; // 移動指針以繼續
  35. }
  36. } while (left <= right);
  37. if (range.start < right) r[p++] = new_Range(range.start, right);
  38. if (range.end > left) r[p++] = new_Range(left, range.end);
  39. }
  40. }

递归法

  1. void swap(int x, int y) {
  2. int t = *x;
  3. x = y;
  4. *y = t;
  5. }
  6. void quick_sort_recursive(int arr[], int start, int end) {
  7. if (start >= end)
  8. return;
  9. int mid = arr[end];
  10. int left = start, right = end - 1;
  11. while (left < right) {
  12. while (arr[left] < mid && left < right)
  13. left++;
  14. while (arr[right] >= mid && left < right)
  15. right--;
  16. swap(&arr[left], &arr[right]);
  17. }
  18. if (arr[left] >= arr[end])
  19. swap(&arr[left], &arr[end]);
  20. else
  21. left++;
  22. if (left)
  23. quick_sort_recursive(arr, start, left - 1);
  24. quick_sort_recursive(arr, left + 1, end);
  25. }
  26. void quick_sort(int arr[], int len) {
  27. quick_sort_recursive(arr, 0, len - 1);
  28. }

C++

函数法

  1. sort(a,a + n);// 排序a[0]-a[n-1]的所有数.

迭代法

  1. // 参考:http://www.dutor.net/index.php/2011/04/recursive-iterative-quick-sort/
  2. struct Range {
  3. int start, end;
  4. Range(int s = 0, int e = 0) {
  5. start = s, end = e;
  6. }
  7. };
  8. template // 整數或浮點數皆可使用, 若要使用物件 (class) 時必須設定 "小於"(<)、"大於"(>)、"不小於"(>=)的運算子功能
  9. void quick_sort(T arr[], const int len) {
  10. if (len <= 0)
  11. return; // 避免 len 等於負值時宣告堆疊陣列當機
  12. // r[] 模擬堆疊, p 為數量, r[p++] 為 push,r[--p] 為 pop 且取得元素
  13. Range r[len];
  14. int p = 0;
  15. r[p++] = Range(0, len - 1);
  16. while (p) {
  17. Range range = r[--p];
  18. if (range.start >= range.end)
  19. continue;
  20. T mid = arr[range.end];
  21. int left = range.start, right = range.end - 1;
  22. while (left < right) {
  23. while (arr[left] < mid && left < right) left++;
  24. while (arr[right] >= mid && left < right) right--;
  25. std::swap(arr[left], arr[right]);
  26. }
  27. if (arr[left] >= arr[range.end])
  28. std::swap(arr[left], arr[range.end]);
  29. else
  30. left++;
  31. r[p++] = Range(range.start, left - 1);
  32. r[p++] = Range(left + 1, range.end);
  33. }
  34. }

递归法

  1. template
  2. void quick_sort_recursive(T arr[], int start, int end) {
  3. if (start >= end)
  4. return;
  5. T mid = arr[end];
  6. int left = start, right = end - 1;
  7. while (left < right) { // 在整个范围内搜寻比枢纽元值小或大的元素,然后将左侧元素与右侧元素交换
  8. while (arr[left] < mid && left < right) // 试图在左侧找到一个比枢纽元更大的元素
  9. left++;
  10. while (arr[right] >= mid && left < right) // 试图在右侧找到一个比枢纽元更小的元素
  11. right--;
  12. std::swap(arr[left], arr[right]); // 交换元素
  13. }
  14. if (arr[left] >= arr[end])
  15. std::swap(arr[left], arr[end]);
  16. else
  17. left++;
  18. quick_sort_recursive(arr, start, left - 1);
  19. quick_sort_recursive(arr, left + 1, end);
  20. }
  21. template // 整數或浮點數皆可使用, 若要使用物件 (class) 時必須設定 "小於"(<)、"大於"(>)、"不小於"(>=)的運算子功能
  22. void quick_sort(T arr[], int len) {
  23. quick_sort_recursive(arr, 0, len - 1);
  24. }

参考地址:

https://github.com/hustcc/JS-Sorting-Algorithm/blob/master/6.quickSort.md

https://zh.wikipedia.org/wiki/快速排序