代码实现
- 相比于基础版的快速排序,随机化的快速排序的改动在于 partition(Comparable[] arr, int l, int r) 方法;
- 基础版在选择 v 的时候,永远选择第1个元素,在近乎有序的数组中,就是n * n的遍历;
- 随机版在选择 v 的时候,从 arr 中任选一个来,换到第一个位置去,算法其余部分不变,从而可以较好的解决近乎有序的数组的问题;
public class QuickSort {
// 我们的算法类不允许产生任何实例
private QuickSort(){}
// 对arr[l...r]部分进行partition操作
// 返回p, 使得arr[l...p-1] < arr[p] ; arr[p+1...r] > arr[p]
private static int partition(Comparable[] arr, int l, int r){
// 随机在arr[l...r]的范围中, 选择一个数值作为标定点pivot
swap(arr, l, (int)(Math.random() * (r - l + 1)) + l);
Comparable v = arr[l];
int j = l; // arr[l+1...j] < v ; arr[j+1...i) > v
for( int i = l + 1 ; i <= r ; i ++ )
if( arr[i].compareTo(v) < 0 ){
j ++;
swap(arr, j, i);
}
swap(arr, l, j);
return j;
}
// 递归使用快速排序,对arr[l...r]的范围进行排序
private static void sort(Comparable[] arr, int l, int r){
// 对于小规模数组, 使用插入排序
if( r - l <= 15 ){
InsertionSort.sort(arr, l, r);
return;
}
int p = partition(arr, l, r);
sort(arr, l, p-1 );
sort(arr, p+1, r);
}
public static void sort(Comparable[] arr){
int n = arr.length;
sort(arr, 0, n-1);
}
private static void swap(Object[] arr, int i, int j) {
Object t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
// 测试 QuickSort
public static void main(String[] args) {
// Quick Sort也是一个O(nlogn)复杂度的算法
// 可以在1秒之内轻松处理100万数量级的数据
int N = 1000000;
Integer[] arr = SortTestHelper.generateRandomArray(N, 0, 100000);
SortTestHelper.testSort("bobo.algo.QuickSort", arr);
return;
}
}
测试代码
import java.util.Arrays;
public class Main {
// 比较Merge Sort和Quick Sort两种排序算法的性能效率
// 两种排序算法虽然都是O(nlogn)级别的, 但是Quick Sort算法有常数级的优势
// Quick Sort要比Merge Sort快, 即使我们对Merge Sort进行了优化
public static void main(String[] args) {
int N = 1000000;
// 测试1 一般性测试
System.out.println("Test for random array, size = " + N + " , random range [0, " + N + "]");
Integer[] arr1 = SortTestHelper.generateRandomArray(N, 0, N);
Integer[] arr2 = Arrays.copyOf(arr1, arr1.length);
SortTestHelper.testSort("_03.sorting_advanced._06.quick_sort_random.MergeSort", arr1);
SortTestHelper.testSort("_03.sorting_advanced._06.quick_sort_random.QuickSort", arr2);
System.out.println();
// 测试2 测试近乎有序的数组
// 加入了随机选择标定点的步骤后, 我们的快速排序可以轻松处理近乎有序的数组
// 但是对于近乎有序的数组, 其效率比优化后的归并排序要低, 但完全再容忍范围里
// 思考一下为什么对于近乎有序的数组, 快排的性能比优化后的归并排序低? :)
int swapTimes = 100;
assert swapTimes >= 0;
System.out.println("Test for nearly ordered array, size = " + N + " , swap time = " + swapTimes);
arr1 = SortTestHelper.generateNearlyOrderedArray(N, swapTimes);
arr2 = Arrays.copyOf(arr1, arr1.length);
SortTestHelper.testSort("_03.sorting_advanced._06.quick_sort_random.MergeSort", arr1);
SortTestHelper.testSort("_03.sorting_advanced._06.quick_sort_random.QuickSort", arr2);
System.out.println();
}
}
输出:
Test for random array, size = 1000000 , random range [0, 1000000]
MergeSort : 442ms
QuickSort : 323ms
Test for nearly ordered array, size = 1000000 , swap time = 100
MergeSort : 87ms
QuickSort : 88ms