本文是用Java实现双轴快速排序,我找不到参考的文章地址了,十分抱歉,在此感谢参考文章的原作者,是你给了我思路和灵感!
双轴快速排序和普通的快速排序不同的地方在于,普通的快速排序选出一个数字,作为一个基准值,然后通过数组值交换的方式,让左边区域的数字都小于基准值,右边区域的数字都大于基准值,然后再对左右两边的区域进行递归快速排序,直到数组全部有序。而双轴快速排序则是选出两个基准值a和b,且a不大于b,然后整个数组同样通过数组值交换的方式,让数组分为三部分,左边的数字小于a,中间的数字在a和b之间,右边的大于b,然后再对这三块区域进行递归快速排序,直到数组全部有序。
比如一个数组:【3,5,2,7,1,4,6】
普通快速排序经过一次排序后是这样的:【(2 1)3(5 7 4 6)】,其中3为排序轴,然后再对括号里的部分递归普通快速排序
双轴快速排序经过一次排序后是这样的:【(21)3(54)6(7)】,其中3和6为排序轴,然后再对括号里的部分递归双轴快速排序
明白了道理,接下来上代码:
import java.util.Arrays;
import java.util.Random;
/**
* @author LiYang
* @ClassName DualPivotQuickSort
* @Description 双轴快速排序算法
* @date 2020/3/24 11:58
*/
public class DualPivotQuickSort {
/**
* 双轴快速排序算法
* @param arr 待排序数组
*/
public static void dualPivotQuickSort(int[] arr) {
//启动双轴快速排序
dualPivotQuickSort(arr, 0, arr.length - 1);
}
/**
* 双轴快速排序递归算法
* @param arr 待排序数组
* @param start 排序范围左下标
* @param end 排序范围右下标
*/
private static void dualPivotQuickSort(int[] arr, int start, int end) {
//下标不等才进行排序,相等则只有一个元素,不用排序
if (start < end) {
//如果最左边的元素比最右边的元素大
if (arr[start] > arr[end]) {
//将之交换,最左边要不大于最右边
swap(arr, start, end);
}
//第一个排序轴为最左边的元素
int pivot1 = arr[start];
//第二个排序轴为最右边的元素
int pivot2 = arr[end];
//设置i为首部下标(左哨兵)
int i = start;
//设置j为尾部下标(右哨兵)
int j = end;
//设置k为首部的下一个的下标,表示当前值(移动哨兵)
int k = start + 1;
//当k没有到达尾部,持续循环
//保持这样的状态:小 i 中 j 大
OUT_LOOP: while (k < j) {
//当前值如果小于左排序轴
if (arr[k] < pivot1) {
//将当前值放在i的左边,i向右移动,k继续下一个
//也就是将arr[k]放在i的左边
swap(arr, ++i, k++);
//如果当前值在两个排序轴的中间
} else if (arr[k] <= pivot2) {
//arr[k]保持在中间,k往右移
k++;
//如果当前值大于右排序轴
} else {
//将j一直向左移动,只要碰到比他大的
while (arr[--j] > pivot2) {
//移动哨兵已经到了或超过右边界,扫描终止
if (j <= k) {
//跳回到标志位OUT_LOOP
break OUT_LOOP;
}
}
//如果遇到的第一个比arr[j]小的比做排序轴还小
if (arr[j] < pivot1) {
//两次交换放到i的左边,i往后移动
swap(arr, j, k);
swap(arr, ++i, k);
//如果遇到的第一个比arr[j]小的是在中间
} else {
//k和j交换,放在k的左边
swap(arr, j, k);
}
//k往右移
k++;
}
}
//最后再让标志位还原初始值
swap(arr, start, i);
swap(arr, end, j);
//递归双轴快排左边的区域
dualPivotQuickSort(arr, start, i - 1);
//递归双轴快排中间的区域
dualPivotQuickSort(arr, i + 1, j - 1);
//递归双轴快排右边的区域
dualPivotQuickSort(arr, j + 1, end);
}
}
/**
* 数组元素交换
* @param arr 数组
* @param i 第一个元素
* @param j 第二个元素
*/
private static void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
/**
* 测试双轴快速排序
* @param args
*/
public static void main(String[] args) {
//待排序数组有30个元素
int[] arr = new int[30];
//随机数类
Random random = new Random();
//初始化生成100以内的随机数
for (int i = 0; i < arr.length; i++) {
arr[i] = random.nextInt(100);
}
System.out.println("双轴快排前:" + Arrays.toString(arr));
//调用双轴快速排序算法
dualPivotQuickSort(arr);
System.out.println("双轴快排后:" + Arrays.toString(arr));
}
}
运行 DualPivotQuickSort 类的 main 方法,验证双轴快速排序算法,控制台打印如下内容,算法测试通过:
双轴快排前:[84, 64, 77, 2, 86, 0, 22, 27, 86, 61, 46, 65, 66, 75, 49, 9, 77, 82, 15, 95, 95, 16, 30, 91, 65, 75, 74, 9, 19, 1]
双轴快排后:[0, 1, 2, 9, 9, 15, 16, 19, 22, 27, 30, 46, 49, 61, 64, 65, 65, 66, 74, 75, 75, 77, 77, 82, 84, 86, 86, 91, 95, 95]