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