算法:用Java实现双轴快速排序(DualPivotQuickSort)

本文是用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]
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章