快速排序
一、算法描述
1.1 核心思想
- 二分,选取一个分区值,将数据分割成 2 部分:小于和大于
- 递归,对小于大于两部分再排序,不断选点分割数据,直到无法分割为止
- 整个二分过程类似于一棵二叉树,从上往下排序,先确定根节点位置,再处理子树排序
1.2 细节解释
比如说, 数组 [5, 7, 2, 4, 3, 6, 1]
。
首先选取一个分区值(随意选一个),比如说 5。
以 5 基准,将其他数据分割成 2 部分:一部分小于 5,一部分大于 5。
1 2 3
| 小于5 大于5 ___ ___ ___ ___ ___ ___ | 2 | 4 | 3 | 1 | | 7 | 6 |
|
然后将 5 插入 2 部分的中间:
1 2 3
| 小于5 大于5 ___ ___ ___ ___ ___ ___ ___ | 2 | 4 | 3 | 1 | | 5 | | 7 | 6 |
|
此时 5 就已经排好了。
下面接着对左右 2 部分进行递归排序。
(1) 先对左边小于 5 的部分排序
首先选取一个分区值 2,然后以 2 为基准分割成 2 部分:
1 2 3
| 小于2 大于2 ___ ___ ___ | 1 | | 4 | 3 |
|
将 2 插入中间位置:
1 2 3
| 小于2 大于2 ___ ___ ___ ___ | 1 | | 2 | | 4 | 3 |
|
此时 2 就排好了。
接着不断往下递归,直到无法分割为止,最后可以得到:
1 2
| ___ ___ ___ ___ | 1 | | 2 | | 3 | | 4 |
|
(2) 再对右边大于 5 的部分排序
同理,也是递归分割数据,最后可以得到:
(3) 最后完整的序列结果
1 2
| ___ ___ ___ ___ ___ ___ ___ | 1 | | 2 | | 3 | | 4 | | 5 | | 6 | | 7 |
|
至此排序就完成了,快速排序本质上是一种分治算法,将大排序变成小排序来做。
二、算法实现
2.1 递归二分排序
1 2 3 4 5 6 7 8 9 10
| void partSort(int[] arr, int start, int end) { if (start < end) { int m = partition(arr, start, end); partSort(arr, start, m); partSort(arr, m + 1, end); } }
|
2.2 对数据进行分区
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| int partition(int[] arr, int start, int end) { int p = pivot(arr, start, end - 1); swap(arr, start, p); int ref = arr[start]; int lp = start; for (int i = lp + 1; i < end; i++) { if (arr[i] <= ref) { swap(arr, ++lp, i); } } swap(arr, start, lp); return lp; }
|
2.3 分区点的选择
1 2 3 4 5 6 7 8 9
| int pivot(int[] arr, int i, int j) { int mid = i + (j - i) / 2; if (arr[i] < arr[j]) { return arr[mid] > arr[i] ? mid : j; } else { return arr[mid] < arr[i] ? mid : i; } }
|
三、算法分析
3.1 时间复杂度
- 最好时间复杂度:O(nlogn)
- 最坏时间复杂度:O(n^2)
- 平均时间复杂度:O(nlogn)
3.2 空间复杂度
3.3 稳定性
四、适用场景
附录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
|
public class QuickSort implements Sort {
@Override public void sort(int[] arr) { partSort(arr, 0, arr.length); }
private void partSort(int[] arr, int start, int end) { if (start < end) { int m = partition(arr, start, end); partSort(arr, start, m); partSort(arr, m + 1, end); } }
private int partition(int[] arr, int start, int end) { int p = pivot(arr, start, end - 1); swap(arr, start, p); int ref = arr[start]; int lp = start; for (int i = lp + 1; i < end; i++) { if (arr[i] <= ref) { swap(arr, ++lp, i); } } swap(arr, start, lp); return lp; }
private int pivot(int[] arr, int i, int j) { int mid = i + (j - i) / 2; if (arr[i] < arr[j]) { return arr[mid] > arr[i] ? mid : j; } else { return arr[mid] < arr[i] ? mid : i; } }
}
|