三向快速排序
一、算法描述
1.1 核心思想
- 三分,选取一个分区值,将数据分割成 3 部分:小于、等于、大于
- 递归,对小于大于两部分继续排序,不断选点分割数据,直到无法分割为止
- 整个过程和快速排序类似,只是原来的一个分区点变成了一个区间而已
1.2 细节解释
比如说, 数组 [3, 5, 2, 1, 3, 5, 1]
。
首先选取一个分区值(随意选一个),比如说 3。
以 3 基准,将其他数据分割成 3 部分:小于 3、等于 3、大于 3。
1 2 3
| 小于3 等于3 大于3 ___ ___ ___ ___ ___ ___ ___ | 2 | 1 | 1 | | 3 | 3 | | 5 | 5 |
|
等于 3 的部分就不用管了,因为已经排好了。
接着对小于 3 和大于 3 的部分进行递归排序。
(1) 先对小于 3 的部分排序
首先选取一个分区值 2,然后以 2 为基准分割成 3 部分:
1 2 3
| 小于2 等于2 大于2 ___ ___ ___ | 1 | 1 | | 2 |
|
等于 2 的部分就不用管了,因为已经排好了。
接着对小于 2 和大于 2 的部分进行递归排序,最终可以得到:
1 2
| ___ ___ ___ | 1 | 1 | 2 |
|
(2) 再对大于 3 的部分排序
一样地,选取一个分区值 5,然后以 5 为基准分割成 3 部分:
1 2 3
| 小于5 等于5 大于5 ___ ___ | 5 | 5 |
|
等于 5 的部分就不用管了,因为已经排好了。
至此,整个排序实际上已经拍好了,结果就是:
1 2
| ___ ___ ___ ___ ___ ___ ___ | 1 | 1 | 2 | 3 | 3 | 5 | 5 |
|
三向快速排序和快速排序差不多,只是中间的单个元素变成了一个区间而已。
但是,如果数组中有大量重复元素,三向快速排序的效率会比快速排序高很多。
二、算法实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| private void partSort(int[] arr, int start, int end) { if (start >= end) { return; } int x = arr[start]; int lp = start - 1, rp = end; for (int i = lp + 1; i < rp; ) { if (arr[i] < x) { swap(arr, i++, ++lp); } else if (arr[i] > x) { swap(arr, i, --rp); } else { i++; } } partSort(arr, start, lp + 1); partSort(arr, rp, end); }
|
三、算法分析
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
|
public class Quick3WaySort 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) { return; } int x = arr[start]; int lp = start - 1, rp = end; for (int i = lp + 1; i < rp; ) { if (arr[i] < x) { swap(arr, i++, ++lp); } else if (arr[i] > x) { swap(arr, i, --rp); } else { i++; } } partSort(arr, start, lp + 1); partSort(arr, rp, end); }
}
|