1、三路快速排序算法简单分析
- 单路、双路排序算法在一定情况下(有序数组或数组元素全部相同时),复杂度会退化称 O ( n 2 ) O(n^2) O(n2),所以需要改写成三路排序算法
- 假设有数组
arr
,需要将arr[l,r]
进行排序。三路快速排序算法的最终目标如图1所示,整个区间被分为3段。
- 其过程可以描述为:首先,随机选取的一个待排序的元素
v
(避免遇到有序数组时,算法退化),将其放在最左边,如图2所示。索引i
指向当前要判断的位置,假设指向的元素为e
,如果e>v
,就将元素e
放到右边紫色的区间,具体方法为将gt++
,交换索引gt
与i
的元素值,并再次判断i
位置元素的大小;如果e<v
,就将元素e
放到左边橙色的区间,具体方法为将lt++
,交换索引lt
与i
的元素值;如果e=v
,i++
判断下一个元素,直到i>=gt
。
- 遍历完整个区间后,整个区间的分布应该如图3所示,下面只要交换索引
l
和索引lt+1
位置的元素就行了。
2、单路、双路、三路快速排序算法比较
- 代码如下所示
import java.util.Arrays;
import java.util.Random;
public class QuickSort {
private QuickSort() {
}
public static <E extends Comparable<E>> void sort(E[] arr) {
Random random = new Random();
sort(arr, 0, arr.length - 1,random);
}
private static <E extends Comparable<E>> void sort(E[] arr, int l, int r,Random random) {
if (l >= r)
return;
int p = partition(arr, l, r,random);
sort(arr, l, p - 1,random);
sort(arr, p + 1, r,random);
}
private static <E extends Comparable<E>> int partition(E[] arr, int l, int r,Random random) {
//生成[l,r]之间的随机索引
int p = random.nextInt(r - l + 1) + l;
swap(arr,l,p);
// 使得arr[l+1,…,j]<v;arr[j+1,…,r]>v;
int j = l;
for (int i = l + 1; i <= r; i++) {
if (arr[i].compareTo(arr[l]) < 0) {
j++;
swap(arr, i, j);
}
}
swap(arr, l, j);
return j;
}
public static <E extends Comparable<E>> void sort2ways(E[] arr) {
Random random = new Random();
sort2ways(arr, 0, arr.length - 1,random);
}
private static <E extends Comparable<E>> void sort2ways(E[] arr, int l, int r,Random random) {
if (l >= r)
return;
int p = partition(arr, l, r,random);
sort2ways(arr, l, p - 1,random);
sort2ways(arr, p + 1, r,random);
}
private static <E extends Comparable<E>> int partition2ways(E[] arr, int l, int r,Random random) {
//生成[l,r]之间的随机索引
int p = random.nextInt(r - l + 1) + l;
swap(arr,l,p);
// 使得arr[l+1,…,j]<=v;arr[j+1,…,r]>=v;
int i = l+1;
int j = r;
while(i <= j){
while (arr[i].compareTo(arr[l]) < 0)
i++;
while (arr[j].compareTo(arr[l]) > 0)
j--;
swap(arr,i,j);
i++;
j--;
}
swap(arr,l,j);
return j;
}
public static <E extends Comparable<E>> void sort3ways(E[] arr) {
Random random = new Random();
sort3ways(arr, 0, arr.length - 1,random);
}
private static <E extends Comparable<E>> void sort3ways(E[] arr, int l, int r,Random random) {
if (l >= r)
return;
// 生成[l,r]之间的随机索引
int p = l + random.nextInt(r - l + 1);
swap(arr,l,p);
// arr[l+1 , lt] < v , arr[lt+1 , i-1] == v ,arr[i , r] > v
int lt = l,i = l + 1, gt = r + 1; // 先将数组初始化为空
while (i < gt){
if (arr[i].compareTo(arr[l]) < 0){
lt++; // 因为lt刚开始指向的是l,所以要先++
swap(arr,lt,i);
i++;
}else if (arr[i].compareTo(arr[l]) > 0){
gt--;
swap(arr,i,gt);
// i 不用++ 因为交换后的数还没有判断其大小
}else {
i++;
}
}
swap(arr,l,lt);
// arr[l , lt-1] < v , arr[lt , gt-1] == v ,arr[gt , r] > v
sort3ways(arr,l,lt-1,random);
sort3ways(arr,gt,r,random);
}
private static <E extends Comparable<E>> void swap(E[] arr, int i, int j) {
E temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
public static void main(String[] args) {
int n = 10000;
// 生成n个范围为[0,n)的随机数
Integer[] arr = ArrayGenerator.generateRandomArray(n,n);
Integer[] arr2 = Arrays.copyOf(arr, arr.length);
Integer[] arr3 = Arrays.copyOf(arr, arr.length);
SortingHelper.sortTest("QuickSort", arr);
SortingHelper.sortTest("QuickSort2ways", arr2);
SortingHelper.sortTest("QuickSort3ways", arr3);
System.out.println("===========");
// 生成n个0
arr = ArrayGenerator.generateRandomArray(n,1);
arr2 = Arrays.copyOf(arr, arr.length);
arr3 = Arrays.copyOf(arr, arr.length);
SortingHelper.sortTest("QuickSort", arr);
SortingHelper.sortTest("QuickSort2ways", arr2);
SortingHelper.sortTest("QuickSort3ways", arr3);
}
}
- 运行结果:
这里就可以看出三路快速排序算法的优越性了!太牛了