快排之java實現

場景

假設我們現在對“6 1 2 7 9 3 4 5 10 8”這個10個數進行升序排序

實現方法

1. 基準數法

  1. 首先在這個序列中隨便找一個數作爲基準數(不要被這個名詞嚇到了,就是一個用來參照的數,待會你就知道它用來做啥的了)爲了方便,就讓第一個數6作爲基準數吧。
  2. 接下來,需要將這個序列中所有比基準數大的數放在6的右邊,比基準數小的數放在6的左邊
  3. 在初始狀態下,數字6在序列的第1位。我們的目標是將6挪到序列中間的某個位置,假設這個位置是k。現在就需要尋找這個k,並且以第k位爲分界點,左邊的數都小於等於6,右邊的數都大於等於6。
  4. 用兩個變量i和j,分別指向序列最左邊和最右邊。即i=1,指向數字6。讓哨兵j指向序列的最右邊(即=10),指向數字8。
  5. 首先哨兵j開始出動。因爲此處設置的基準數是最左邊的數,所以需要讓哨兵j先出動,這一點非常重要(請自己想一想爲什麼)。哨兵j一步一步地向左挪動(即j–),直到找到一個小於6的數停下來。接下來哨兵i再一步一步向右挪動(即i++),直到找到一個數大於6的數停下來。最後哨兵j停在了數字5面前,哨兵i停在了數字7面前。
  6. 現在交換哨兵i和哨兵j所指向的元素的值(至少交換值,i、j位置不變)。交換之後的序列如下:

x x x x i x x x x j
6 1 2 5 9 3 4 7 10 8 (第一次交換結果)
以此類推:
x x x x x i x x j x x
6 1 2 5 4 3 9 7 10 8 (第二次交換結果)
x x x x x x ij x x
6 1 2 5 4 3 9 7 10 8 (第三次交換結果)

  1. 哨兵i和哨兵j都走到3面前。說明此時“探測”結束。我們將基準數6和3進行交換。交換之後的序列如下:

3 1 2 5 4 6 9 7 10 8
到此第一輪“探測”真正結束
回顧一下剛纔的過程,其實哨兵j的使命就是要找小於基準數的數,而哨兵i的使命就是要找大於基準數的數,直到i和j碰頭爲止。

  1. 此時我們已經將原來的序列,以6爲分界點拆分成了兩個序列,左邊的序列是“3 1 2 5 4”,右邊的序列是“9 7 10 8”。接下來還需要分別處理這兩個序列。因爲6左邊和右邊的序列目前都還是很混亂的。不過不要緊,我們已經掌握了方法,接下來只要模擬剛纔的方法分別處理6左邊和右邊的序列即可。現在先來處理6左邊的序列現吧。最終結果如下:

1 2 3 4 5 6 7 8 9 10

  1. 其實上述方法是利用了二分法的思想
  2. 代碼實現
package com.example.sort;

import java.util.Arrays;

public class QuickSortDemo01 {
    public static void quickSort(Integer[] arr, int low, int high) {
        int i, j, temp, t;
        if (low > high) {
            return;
        }
        i = low;
        j = high;
        //temp就是基準位
        temp = arr[low];

        while (i < j) {
            //先從右邊,依次往左遞減
            while (temp <= arr[j] && i < j) {
                j--;
            }
            //再從左邊,依次往右遞增
            while (temp >= arr[i] && i < j) {
                i++;
            }
            //如果滿足條件則交換
            if (i < j) {
                t = arr[j];
                arr[j] = arr[i];
                arr[i] = t;
            }

        }
        //最後將基準爲與i和j相等位置的數字交換
        arr[low] = arr[i];
        arr[i] = temp;
        //遞歸調用左半數組
        quickSort(arr, low, j - 1);
        //遞歸調用右半數組
        quickSort(arr, j + 1, high);
    }


    public static void main(String[] args) {
        Integer[] arr = {16, 71, 2, 4, 7, 62, 13, 4, 2, 1, 8, 9, 19};
        quickSort(arr, 0, arr.length - 1);
        System.out.println(Arrays.asList(arr));       
    }
}
// [1, 2, 2, 4, 4, 7, 8, 9, 13, 16, 19, 62, 71]

2. 三數取中法

  1. 在上面基準數法中快排的過程中,每一次我們要取一個元素作爲樞紐值,以這個數字來將序列劃分爲兩部分。
  2. 在此我們採用三數取中法,也就是取左端、中間、右端三個數,然後進行(升序)排序,將中間數作爲樞紐值。
  3. 原始數據

4 5 7 8 1 2 3 6
此處中間數8是根據取模 左端[下標0]和右端[下標7]之和取模[ (0+7)/2 = 3 ]得到,8的下標爲3.

  1. 對4、8、6進行排序[ 4 6 8 ]並且把樞紐值6放在數組最後 [ 8之前 ]

4 5 7 1 2 3 6 8

  1. 根據樞紐值6進行分割

4 5 7 1 2 3 6 8
對分割之後的 5 7 1 2 3 採用基準數法進行排序
4 5 2 3 1 6 7 8 ( 第一輪分割完成)

  1. 遞歸在子序列進行上述相同處理(先三位取中,再以中值分割)

4 5 2 3 1 (左子序列)
7 8 ( 右子序列)
右子序列因爲已經是有序的,所以不用處理;對左子序列進行處理即可

  1. 最終排序結果爲:

1 2 3 4 5 6 7 8

  1. 代碼實現:
package com.example.sort;

import java.util.Arrays;

public class QuickSortDemo02 {
    public static void main(String[] args) {
        int[] arr = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
        quickSort(arr, 0, arr.length - 1);
        System.out.println("排序結果:" + Arrays.toString(arr));
    }

    /**
     * 排序
     */
    public static void quickSort(int[] arr, int left, int right) {
        if (left < right) {
            //獲取樞紐值,並將其放在當前待處理序列末尾
            dealPivot(arr, left, right);
            //樞紐值被放在序列末尾
            int pivot = right - 1;
            //左指針
            int i = left;
            //右指針
            int j = right - 1;
            while (true) {
                while (arr[++i] < arr[pivot]) {
                }
                while (j > left && arr[--j] > arr[pivot]) {
                }
                if (i < j) {
                    swap(arr, i, j);
                } else {
                    break;
                }
            }
            if (i < right) {
                swap(arr, i, right - 1);
            }
            quickSort(arr, left, i - 1);
            quickSort(arr, i + 1, right);
        }

    }

    /**
     * 處理樞紐值
     */
    public static void dealPivot(int[] arr, int left, int right) {
        int mid = (left + right) / 2;
        if (arr[left] > arr[mid]) {
            swap(arr, left, mid);
        }
        if (arr[left] > arr[right]) {
            swap(arr, left, right);
        }
        if (arr[right] < arr[mid]) {
            swap(arr, right, mid);
        }
        swap(arr, right - 1, mid);
    }

    /**
     * 交換元素通用處理
     */
    private static void swap(int[] arr, int a, int b) {
        int temp = arr[a];
        arr[a] = arr[b];
        arr[b] = temp;
    }
}
// 排序結果:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章