辅助工具
- 数组工具类 : https://blog.csdn.net/love905661433/article/details/83106078
- 用于比较的归并排序代码 : https://blog.csdn.net/love905661433/article/details/83118153
概述
快速排序由C. A. R. Hoare在1962年提出。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
简单来说, 就是设定一个标准V, 每次遍历就将整个数组分成小于V和大于V的两部分, 这时,V就处于它在排好序的数组中应该在的位置, 然后再对左右两部分分别进行递归排序, 这个将数组分割成两部分的操作, 称之为partition, 下面看下代码实现 :
代码实现
package sort.quick;
import sort.Sort ;
import utils.ArrayUtil ;
public class QuickSort implements Sort {
@Override
public void sort(int [] arr) {
quickSort(arr, 0, arr.length - 1);
}
/**
* 对数组arr的[l, r]前闭后闭的区间进行快速排序
* 递归方法
* @param arr
* @param l
* @param r
*/
private void quickSort(int [] arr, int l, int r){
// 递归终止条件
if (l >= r) {
return;
}
int p = partition(arr, l, r);
quickSort(arr, l, p - 1);
quickSort(arr, p + 1, r);
}
/**
* 对arr数组的前闭后闭区间[l, r]进行partition操作
* @param arr
* @param l
* @param r
* @return 返回分割后作为标记元素的下标位置
*/
private int partition(int [] arr, int l, int r){
// 设置用于对比的元素为arr[l]
int v = arr[l];
// arr[l + 1, j] <v; arr[j+1, i)>v
int j = l;
for (int i = l; i <= r; i++) {
if (arr[i] < v) {
ArrayUtil.swap(arr, i, j + 1);
j++;
}
}
ArrayUtil.swap(arr, l, j);
return j;
}
}
下面我们就可以测试一下快速排序和归并排序的性能之间的差异, 测试代码如下 :
/**
* 测试插入排序和归并排序性能
*/
@Test
public void testSort(){
// 生成一个随机数组
int[] arr = ArrayUtil.generateArray(1000000, 0, 1000000);
int[] arr1 = ArrayUtil.copyArray(arr);
int[] arr2 = ArrayUtil.copyArray(arr);
int[] arr3 = ArrayUtil.copyArray(arr);
int[] arr4 = ArrayUtil.copyArray(arr);
System.out.println("----------------------------------随机数组----------------------------------") ;
System.out.println("归并排序1 : " + ArrayUtil.testSort(arr1, new MergeSort()) + "s") ;
System.out.println("归并排序2 : " + ArrayUtil.testSort(arr2, new MergeSort2()) + "s") ;
System.out.println("归并排序3 : " + ArrayUtil.testSort(arr3, new MergeSort3()) + "s") ;
System.out.println("自底向上的归并排序 : " + ArrayUtil.testSort(arr4, new MergeSortBU()) + "s") ;
System.out.println("快速排序 : " + ArrayUtil.testSort(arr, new QuickSort()) + "s") ;
/*
* 生成一个近乎有序的数组
* 100000 : 数组元素个数
* 10 : 在一个完全有序的数组上进行多少次元素交换
*/
arr = ArrayUtil.generateNearlyOrderedArray(1000000, 10);
arr1 = ArrayUtil.copyArray(arr);
arr2 = ArrayUtil.copyArray(arr);
arr3 = ArrayUtil.copyArray(arr);
arr4 = ArrayUtil.copyArray(arr);
System.out.println("------------------------------近乎有序的数组------------------------------") ;
System.out.println("归并排序1:" + ArrayUtil.testSort(arr1, new MergeSort()) + "s") ;
System.out.println("归并排序2:" + ArrayUtil.testSort(arr2, new MergeSort2()) + "s") ;
System.out.println("归并排序3:" + ArrayUtil.testSort(arr3, new MergeSort3()) + "s") ;
System.out.println("自底向上的归并排序:" + ArrayUtil.testSort(arr4, new MergeSortBU()) + "s") ;
System.out.println("快速排序 : " + ArrayUtil.testSort(arr, new QuickSort()) + "s") ;
/*
* 生成一个存在大量重复元素的数组
* 100000 : 数组元素个数
*/
arr = ArrayUtil.generateArray(1000000, 0, 100);
arr1 = ArrayUtil.copyArray(arr);
arr2 = ArrayUtil.copyArray(arr);
arr3 = ArrayUtil.copyArray(arr);
arr4 = ArrayUtil.copyArray(arr);
System.out.println("------------------------------存在大量重复元素的数组------------------------------") ;
System.out.println("归并排序1:" + ArrayUtil.testSort(arr1, new MergeSort()) + "s") ;
System.out.println("归并排序2:" + ArrayUtil.testSort(arr2, new MergeSort2()) + "s") ;
System.out.println("归并排序3:" + ArrayUtil.testSort(arr3, new MergeSort3()) + "s") ;
System.out.println("自底向上的归并排序:" + ArrayUtil.testSort(arr4, new MergeSortBU()) + "s") ;
System.out.println("快速排序 : " + ArrayUtil.testSort(arr, new QuickSort()) + "s") ;
}
测试结果如下 :
----------------------------------随机数组----------------------------------
归并排序1 : 0.205s
归并排序2 : 0.148s
归并排序3 : 0.154s
自底向上的归并排序 : 0.162s
快速排序 : 0.109s
------------------------------近乎有序的数组------------------------------
归并排序1:0.082s
归并排序2:0.047s
归并排序3:0.016s
自底向上的归并排序:0.02s
快速排序 : 60.363s
------------------------------存在大量重复元素的数组------------------------------
归并排序1:0.154s
归并排序2:0.114s
归并排序3:0.09s
自底向上的归并排序:0.09s
快速排序 : 3.401s
对于100万的数据量, 从上面的测试结果来说, 可以得到下面的结论 :
- 对于随机数组来说, 快速排序的性能已经高于了归并排序了
- 对于近乎有序的数组, 由于这个版本的在partition的过程中每次都取第一个元素作为标准, 这会导致partition分割的两部分严重不均衡, 在极端情况下即数组完全有序的情况下, 会退化成O(n²)级别的算法, 所以这个排序花了60多秒, 这个时间显然是不能接受的, 下面会进行优化
- 对于存在大量重复元素的数组, 上面的代码其实是分成小于等于V和大于V的两部分, 一样是会造成partition的不平衡, 下面是对这些问题的优化方案
需要注意的一点是, 在对近乎有序的数组使用这一版的归并排序时, 会因为递归深度较深而占用大量栈空间, 如果报StackOVerflow的异常, 可以使用参数-Xss将栈空间设置大一点, 我这里设置了-Xss128m是没有问题的
排序优化
优化1
在上一篇归并排序的文章中就说过, 对于数据量较小的数组, 使用插入排序的性能反而更快, 所以可以在partition到底层数据量较小的时候, 使用插入排序替换快速排序, 进行第一次优化 :
package sort.quick;
import sort.Sort ;
import utils.ArrayUtil ;
/**
* 快速排序优化
* @author xuxiumeng
*
*/
public class QuickSort2 implements Sort {
@Override
public void sort(int [] arr) {
quickSort(arr, 0, arr.length - 1);
}
/**
* 对数组arr的[l, r]前闭后闭的区间进行快速排序
* 递归方法
* @param arr
* @param l
* @param r
*/
private void quickSort(int [] arr, int l, int r){
// 递归终止条件
if ( r - l < 16) {
ArrayUtil.insertSort(arr, l, r);
return;
}
int p = partition(arr, l, r);
quickSort(arr, l, p - 1);
quickSort(arr, p + 1, r);
}
/**
* 对arr数组的前闭后闭区间[l, r]进行partition操作
* @param arr
* @param l
* @param r
* @return 返回分割后作为标记元素的下标位置
*/
private int partition(int [] arr, int l, int r){
// 设置用于对比的元素为arr[l]
int v = arr[l];
// arr[l + 1, j] <v; arr[j+1, i)>v
int j = l;
for (int i = l; i <= r; i++) {
if (arr[i] < v) {
ArrayUtil.swap(arr, i, j + 1);
j++;
}
}
ArrayUtil.swap(arr, l, j);
return j;
}
}
测试代码 :
/**
* 测试插入排序和归并排序性能
*/
@Test
public void testSort(){
// 生成一个随机数组
int[] arr = ArrayUtil.generateArray(1000000, 0, 1000000);
int[] arr1 = ArrayUtil.copyArray(arr);
int[] arr2 = ArrayUtil.copyArray(arr);
int[] arr3 = ArrayUtil.copyArray(arr);
int[] arr4 = ArrayUtil.copyArray(arr);
int[] arr5 = ArrayUtil.copyArray(arr);
System.out.println("----------------------------------随机数组----------------------------------") ;
System.out.println("归并排序1 : " + ArrayUtil.testSort(arr1, new MergeSort()) + "s") ;
System.out.println("归并排序2 : " + ArrayUtil.testSort(arr2, new MergeSort2()) + "s") ;
System.out.println("归并排序3 : " + ArrayUtil.testSort(arr3, new MergeSort3()) + "s") ;
System.out.println("自底向上的归并排序 : " + ArrayUtil.testSort(arr4, new MergeSortBU()) + "s") ;
System.out.println("快速排序 : " + ArrayUtil.testSort(arr, new QuickSort()) + "s") ;
System.out.println("快速排序2 : " + ArrayUtil.testSort(arr5, new QuickSort2()) + "s") ;
/*
* 生成一个近乎有序的数组
* 100000 : 数组元素个数
* 10 : 在一个完全有序的数组上进行多少次元素交换
*/
arr = ArrayUtil.generateNearlyOrderedArray(1000000, 10);
arr1 = ArrayUtil.copyArray(arr);
arr2 = ArrayUtil.copyArray(arr);
arr3 = ArrayUtil.copyArray(arr);
arr4 = ArrayUtil.copyArray(arr);
arr5 = ArrayUtil.copyArray(arr);
System.out.println("------------------------------近乎有序的数组------------------------------") ;
System.out.println("归并排序1:" + ArrayUtil.testSort(arr1, new MergeSort()) + "s") ;
System.out.println("归并排序2:" + ArrayUtil.testSort(arr2, new MergeSort2()) + "s") ;
System.out.println("归并排序3:" + ArrayUtil.testSort(arr3, new MergeSort3()) + "s") ;
System.out.println("自底向上的归并排序:" + ArrayUtil.testSort(arr4, new MergeSortBU()) + "s") ;
System.out.println("快速排序 : " + ArrayUtil.testSort(arr, new QuickSort()) + "s") ;
System.out.println("快速排序2 : " + ArrayUtil.testSort(arr5, new QuickSort2()) + "s") ;
/*
* 生成一个存在大量重复元素的数组
* 100000 : 数组元素个数
*/
arr = ArrayUtil.generateArray(1000000, 0, 100);
arr1 = ArrayUtil.copyArray(arr);
arr2 = ArrayUtil.copyArray(arr);
arr3 = ArrayUtil.copyArray(arr);
arr4 = ArrayUtil.copyArray(arr);
arr5 = ArrayUtil.copyArray(arr);
System.out.println("------------------------------存在大量重复元素的数组------------------------------") ;
System.out.println("归并排序1:" + ArrayUtil.testSort(arr1, new MergeSort()) + "s") ;
System.out.println("归并排序2:" + ArrayUtil.testSort(arr2, new MergeSort2()) + "s") ;
System.out.println("归并排序3:" + ArrayUtil.testSort(arr3, new MergeSort3()) + "s") ;
System.out.println("自底向上的归并排序:" + ArrayUtil.testSort(arr4, new MergeSortBU()) + "s") ;
System.out.println("快速排序 : " + ArrayUtil.testSort(arr, new QuickSort()) + "s") ;
System.out.println("快速排序2 : " + ArrayUtil.testSort(arr5, new QuickSort2()) + "s") ;
}
测试结果 :
----------------------------------随机数组----------------------------------
归并排序1 : 0.191s
归并排序2 : 0.143s
归并排序3 : 0.156s
自底向上的归并排序 : 0.16s
快速排序 : 0.109s
快速排序2 : 0.102s
------------------------------近乎有序的数组------------------------------
归并排序1:0.082s
归并排序2:0.047s
归并排序3:0.014s
自底向上的归并排序:0.021s
快速排序 : 134.718s
快速排序2 : 133.616s
------------------------------存在大量重复元素的数组------------------------------
归并排序1:0.183s
归并排序2:0.109s
归并排序3:0.086s
自底向上的归并排序:0.09s
快速排序 : 3.495s
快速排序2 : 3.481s
从测试结果看出, 这一版的快速排序比上一版的快速排序性能上要稍微好一点, 但是并没有借据近乎有序的数组, 和存在大量重复元素的数组性能较差的问题, 所以还需要继续优化
优化2
因为数组可能是近乎有序的数组, 所以我们可以不每次取队首的元素, 一种方案是每次随机取一个元素, 一种方案是取队首, 中间, 队尾三个元素中取大小排在中间的那个, 这里我们就使用随机元素的方案, 代码如下:
package sort.quick;
import java.util.Random;
import sort.Sort ;
import utils.ArrayUtil ;
/**
* 快速排序优化
* 上面两个版本的快速排序, 在处理近乎有序的数组的时候,性能很差,
* 因为每次partition时都取队首元素, 在数组近乎有序的时候, 会导致partition的两部分很不均匀
* 在数组完全有序的情况下, 快速排序甚至会退化成O(n²)算法
* 优化方案 : 每次随机取一个元素, 而不是每次取第一个元素
* @author xuxiumeng
*
*/
public class QuickSort3 implements Sort {
private static Random random = new Random();
@Override
public void sort(int [] arr) {
quickSort(arr, 0, arr.length - 1);
}
/**
* 对数组arr的[l, r]前闭后闭的区间进行快速排序
* 递归方法
* @param arr
* @param l
* @param r
*/
private void quickSort(int [] arr, int l, int r){
// 递归终止条件
if ( r - l < 16) {
ArrayUtil.insertSort(arr, l, r);
return;
}
int p = partition(arr, l, r);
quickSort(arr, l, p - 1);
quickSort(arr, p + 1, r);
}
/**
* 对arr数组的前闭后闭区间[l, r]进行partition操作
* @param arr
* @param l
* @param r
* @return 返回分割后作为标记元素的下标位置
*/
private int partition(int [] arr, int l, int r){
// 获取用于对比的随机元素下标
int randomNum = random.nextInt(r - l) + l;
ArrayUtil.swap(arr, l, randomNum);
// 设置用于对比的元素为arr[l]
int v = arr[l];
// arr[l + 1, j] <v; arr[j+1, i)>v
int j = l;
for (int i = l; i <= r; i++) {
if (arr[i] < v) {
ArrayUtil.swap(arr, i, j + 1);
j++;
}
}
ArrayUtil.swap(arr, l, j);
return j;
}
}
相比上一版, 这一版在排序的过程中仅仅多了如下两行代码 :
// 获取用于对比的随机元素下标
int randomNum = random.nextInt(r - l) + l;
ArrayUtil.swap(arr, l, randomNum);
测试代码 :
/**
* 测试插入排序和归并排序性能
*/
@Test
public void testSort(){
// 生成一个随机数组
int[] arr = ArrayUtil.generateArray(1000000, 0, 1000000);
int[] arr1 = ArrayUtil.copyArray(arr);
int[] arr2 = ArrayUtil.copyArray(arr);
int[] arr3 = ArrayUtil.copyArray(arr);
int[] arr4 = ArrayUtil.copyArray(arr);
int[] arr5 = ArrayUtil.copyArray(arr);
int[] arr6 = ArrayUtil.copyArray(arr);
// int[] arr7 = ArrayUtil.copyArray(arr);
System.out.println("----------------------------------随机数组----------------------------------") ;
System.out.println("归并排序1 : " + ArrayUtil.testSort(arr1, new MergeSort()) + "s") ;
System.out.println("归并排序2 : " + ArrayUtil.testSort(arr2, new MergeSort2()) + "s") ;
System.out.println("归并排序3 : " + ArrayUtil.testSort(arr3, new MergeSort3()) + "s") ;
System.out.println("自底向上的归并排序 : " + ArrayUtil.testSort(arr4, new MergeSortBU()) + "s") ;
System.out.println("快速排序 : " + ArrayUtil.testSort(arr, new QuickSort()) + "s") ;
System.out.println("快速排序2 : " + ArrayUtil.testSort(arr5, new QuickSort2()) + "s") ;
System.out.println("快速排序3 : " + ArrayUtil.testSort(arr6, new QuickSort3()) + "s") ;
// System.out.println("三路快速排序 : " + ArrayUtil.testSort(arr7, new QuickSort3Ways()) + "s") ;
/*
* 生成一个近乎有序的数组
* 100000 : 数组元素个数
* 10 : 在一个完全有序的数组上进行多少次元素交换
*/
arr = ArrayUtil.generateNearlyOrderedArray(1000000, 10);
arr1 = ArrayUtil.copyArray(arr);
arr2 = ArrayUtil.copyArray(arr);
arr3 = ArrayUtil.copyArray(arr);
arr4 = ArrayUtil.copyArray(arr);
arr5 = ArrayUtil.copyArray(arr);
arr6 = ArrayUtil.copyArray(arr);
// arr7 = ArrayUtil.copyArray(arr);
System.out.println("------------------------------近乎有序的数组------------------------------") ;
System.out.println("归并排序1:" + ArrayUtil.testSort(arr1, new MergeSort()) + "s") ;
System.out.println("归并排序2:" + ArrayUtil.testSort(arr2, new MergeSort2()) + "s") ;
System.out.println("归并排序3:" + ArrayUtil.testSort(arr3, new MergeSort3()) + "s") ;
System.out.println("自底向上的归并排序:" + ArrayUtil.testSort(arr4, new MergeSortBU()) + "s") ;
System.out.println("快速排序 : " + ArrayUtil.testSort(arr, new QuickSort()) + "s") ;
System.out.println("快速排序2 : " + ArrayUtil.testSort(arr5, new QuickSort2()) + "s") ;
System.out.println("快速排序3 : " + ArrayUtil.testSort(arr6, new QuickSort3()) + "s") ;
// System.out.println("三路快速排序 : " + ArrayUtil.testSort(arr7, new QuickSort3Ways()) + "s") ;
/*
* 生成一个存在大量重复元素的数组
* 100000 : 数组元素个数
*/
arr = ArrayUtil.generateArray(1000000, 0, 100);
arr1 = ArrayUtil.copyArray(arr);
arr2 = ArrayUtil.copyArray(arr);
arr3 = ArrayUtil.copyArray(arr);
arr4 = ArrayUtil.copyArray(arr);
arr5 = ArrayUtil.copyArray(arr);
arr6 = ArrayUtil.copyArray(arr);
// arr7 = ArrayUtil.copyArray(arr);
System.out.println("------------------------------存在大量重复元素的数组------------------------------") ;
System.out.println("归并排序1:" + ArrayUtil.testSort(arr1, new MergeSort()) + "s") ;
System.out.println("归并排序2:" + ArrayUtil.testSort(arr2, new MergeSort2()) + "s") ;
System.out.println("归并排序3:" + ArrayUtil.testSort(arr3, new MergeSort3()) + "s") ;
System.out.println("自底向上的归并排序:" + ArrayUtil.testSort(arr4, new MergeSortBU()) + "s") ;
System.out.println("快速排序 : " + ArrayUtil.testSort(arr, new QuickSort()) + "s") ;
System.out.println("快速排序2 : " + ArrayUtil.testSort(arr5, new QuickSort2()) + "s") ;
System.out.println("快速排序3 : " + ArrayUtil.testSort(arr6, new QuickSort3()) + "s") ;
// System.out.println("三路快速排序 : " + ArrayUtil.testSort(arr7, new QuickSort3Ways()) + "s") ;
}
测试结果 :
----------------------------------随机数组----------------------------------
归并排序1 : 0.189s
归并排序2 : 0.142s
归并排序3 : 0.144s
自底向上的归并排序 : 0.15s
快速排序 : 0.115s
快速排序2 : 0.095s
快速排序3 : 0.099s
------------------------------近乎有序的数组------------------------------
归并排序1:0.082s
归并排序2:0.048s
归并排序3:0.016s
自底向上的归并排序:0.023s
快速排序 : 89.75s
快速排序2 : 83.944s
快速排序3 : 0.032s
------------------------------存在大量重复元素的数组------------------------------
归并排序1:0.177s
归并排序2:0.096s
归并排序3:0.088s
自底向上的归并排序:0.09s
快速排序 : 2.99s
快速排序2 : 2.803s
快速排序3 : 3.454s
可以看到仅仅多了两行代码, 这种情况下对于近乎有序的数组的排序性能提高了很多, 只是对于存在大量重复元素的数组, 排序性能还需要进一步进行优化
优化3 : 三路快排
对于存在大量重复元素的数组, 我们可以不仅仅将数组分割成两部分, 而是可以将数组分割成小于V, 等于V, 大于V的三部分, 然后对小于V和大于V的两部分在进行递归排序, 代码如下:
package sort.quick;
import java.util.Random;
import sort.Sort ;
import utils.ArrayUtil ;
/**
* 快速排序优化
* 上面快速排序的三个版本仍然存在一定的问题, 对于存在大量重复元素的数组,
* 排序的性能是远远低于归并排序的,
* 原因是因为每次进行partition实际是把数组分成小于等于v的元素和大于v的元素, 由于存在大量重复的元素
* 所以会导致partition分割的两部分很不平衡
*
* 优化方案 : 三路快排, 将数组分割成大于V,等于V, 小于V的三部分
* @author xuxiumeng
*
*/
public class QuickSort3Ways implements Sort {
private static Random random = new Random();
@Override
public void sort(int [] arr) {
quickSort3Ways(arr, 0, arr.length - 1);
}
/**
* 对数组arr的[l, r]前闭后闭的区间进行快速排序
* 递归方法
* @param arr
* @param l
* @param r
*/
private void quickSort3Ways(int [] arr, int l, int r){
// 递归终止条件
if ( r - l < 16) {
ArrayUtil.insertSort(arr, l, r);
return;
}
// 获取用于对比的随机元素下标
int randomNum = random.nextInt(r - l) + l;
ArrayUtil.swap(arr, l, randomNum);
// 设置用于对比的元素为arr[l]
int v = arr[l];
// 设定arr[l+1...lt] < v, 初始情况下这个区间为空, 默认符合这个定义
int lt = l;
// 设定arr[gt...r] > v, 初始情况下这个区间为空, 默认符合这个定义
int gt = r + 1;
// arr[lt+1...i) == v, 初始情况下这个区间为空, 默认符合这个定义
int i = l+1;
// 在整个遍历过程中都保持arr[l+1...lt] < v, arr[gt...r] > v, arr[lt+1...i) == v
while (i < gt) {
if (arr[i] < v ) {
ArrayUtil.swap(arr, i, lt + 1);
lt++;
i++;
} else if (arr[i] > v) {
// 这种情况下i的位置是从gt-1交换过来的元素, 仍然需要对这个元素进行判断, 所以这里不需要i++;
ArrayUtil.swap(arr, i, gt - 1);
gt--;
} else {
i++;
}
}
ArrayUtil.swap(arr, l, lt);
quickSort3Ways(arr, l, lt - 1);
quickSort3Ways(arr, gt, r);
}
}
测试代码 :
/**
* 测试插入排序和归并排序性能
*/
@Test
public void testSort(){
// 生成一个随机数组
int[] arr = ArrayUtil.generateArray(1000000, 0, 1000000);
int[] arr1 = ArrayUtil.copyArray(arr);
int[] arr2 = ArrayUtil.copyArray(arr);
int[] arr3 = ArrayUtil.copyArray(arr);
int[] arr4 = ArrayUtil.copyArray(arr);
int[] arr5 = ArrayUtil.copyArray(arr);
int[] arr6 = ArrayUtil.copyArray(arr);
int[] arr7 = ArrayUtil.copyArray(arr);
System.out.println("----------------------------------随机数组----------------------------------") ;
System.out.println("归并排序1 : " + ArrayUtil.testSort(arr1, new MergeSort()) + "s") ;
System.out.println("归并排序2 : " + ArrayUtil.testSort(arr2, new MergeSort2()) + "s") ;
System.out.println("归并排序3 : " + ArrayUtil.testSort(arr3, new MergeSort3()) + "s") ;
System.out.println("自底向上的归并排序 : " + ArrayUtil.testSort(arr4, new MergeSortBU()) + "s") ;
System.out.println("快速排序 : " + ArrayUtil.testSort(arr, new QuickSort()) + "s") ;
System.out.println("快速排序2 : " + ArrayUtil.testSort(arr5, new QuickSort2()) + "s") ;
System.out.println("快速排序3 : " + ArrayUtil.testSort(arr6, new QuickSort3()) + "s") ;
System.out.println("三路快速排序 : " + ArrayUtil.testSort(arr7, new QuickSort3Ways()) + "s") ;
/*
* 生成一个近乎有序的数组
* 100000 : 数组元素个数
* 10 : 在一个完全有序的数组上进行多少次元素交换
*/
arr = ArrayUtil.generateNearlyOrderedArray(1000000, 10);
arr1 = ArrayUtil.copyArray(arr);
arr2 = ArrayUtil.copyArray(arr);
arr3 = ArrayUtil.copyArray(arr);
arr4 = ArrayUtil.copyArray(arr);
arr5 = ArrayUtil.copyArray(arr);
arr6 = ArrayUtil.copyArray(arr);
arr7 = ArrayUtil.copyArray(arr);
System.out.println("------------------------------近乎有序的数组------------------------------") ;
System.out.println("归并排序1:" + ArrayUtil.testSort(arr1, new MergeSort()) + "s") ;
System.out.println("归并排序2:" + ArrayUtil.testSort(arr2, new MergeSort2()) + "s") ;
System.out.println("归并排序3:" + ArrayUtil.testSort(arr3, new MergeSort3()) + "s") ;
System.out.println("自底向上的归并排序:" + ArrayUtil.testSort(arr4, new MergeSortBU()) + "s") ;
System.out.println("快速排序 : " + ArrayUtil.testSort(arr, new QuickSort()) + "s") ;
System.out.println("快速排序2 : " + ArrayUtil.testSort(arr5, new QuickSort2()) + "s") ;
System.out.println("快速排序3 : " + ArrayUtil.testSort(arr6, new QuickSort3()) + "s") ;
System.out.println("三路快速排序 : " + ArrayUtil.testSort(arr7, new QuickSort3Ways()) + "s") ;
/*
* 生成一个存在大量重复元素的数组
* 100000 : 数组元素个数
*/
arr = ArrayUtil.generateArray(1000000, 0, 100);
arr1 = ArrayUtil.copyArray(arr);
arr2 = ArrayUtil.copyArray(arr);
arr3 = ArrayUtil.copyArray(arr);
arr4 = ArrayUtil.copyArray(arr);
arr5 = ArrayUtil.copyArray(arr);
arr6 = ArrayUtil.copyArray(arr);
arr7 = ArrayUtil.copyArray(arr);
System.out.println("------------------------------存在大量重复元素的数组------------------------------") ;
System.out.println("归并排序1:" + ArrayUtil.testSort(arr1, new MergeSort()) + "s") ;
System.out.println("归并排序2:" + ArrayUtil.testSort(arr2, new MergeSort2()) + "s") ;
System.out.println("归并排序3:" + ArrayUtil.testSort(arr3, new MergeSort3()) + "s") ;
System.out.println("自底向上的归并排序:" + ArrayUtil.testSort(arr4, new MergeSortBU()) + "s") ;
System.out.println("快速排序 : " + ArrayUtil.testSort(arr, new QuickSort()) + "s") ;
System.out.println("快速排序2 : " + ArrayUtil.testSort(arr5, new QuickSort2()) + "s") ;
System.out.println("快速排序3 : " + ArrayUtil.testSort(arr6, new QuickSort3()) + "s") ;
System.out.println("三路快速排序 : " + ArrayUtil.testSort(arr7, new QuickSort3Ways()) + "s") ;
}
测试结果 :
----------------------------------随机数组----------------------------------
归并排序1 : 0.197s
归并排序2 : 0.137s
归并排序3 : 0.163s
自底向上的归并排序 : 0.143s
快速排序 : 0.107s
快速排序2 : 0.102s
快速排序3 : 0.109s
三路快速排序 : 0.13s
------------------------------近乎有序的数组------------------------------
归并排序1:0.09s
归并排序2:0.05s
归并排序3:0.019s
自底向上的归并排序:0.026s
快速排序 : 92.938s
快速排序2 : 87.269s
快速排序3 : 0.035s
三路快速排序 : 0.064s
------------------------------存在大量重复元素的数组------------------------------
归并排序1:0.167s
归并排序2:0.092s
归并排序3:0.088s
自底向上的归并排序:0.088s
快速排序 : 3.503s
快速排序2 : 3.389s
快速排序3 : 2.252s
三路快速排序 : 0.036s
从测试结果来看, 三路快排基本上解决了各种问题, 虽然在对随机数组排序的性能上比之前几种稍差一点, 但是完全是在能接受的范围内, 影响不大
总结
- O(nlogn)级别算法
- 非稳定性算法, 算法稳定性可以参见上一篇递归排序里面的定义