本文是用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]