排序算法 | 快速排序算法原理及實現和優化(二)

接上文《數據結構和算法 | 快速排序算法原理及實現和優化(一)》,我們來講講快速排序的五種優化方案。

1、優化選取基準點

三數取中法:先找出三個關鍵字,然後排序,取中間的關鍵字(至少不會是兩個極端)。在代碼示例中我們選取相應區間的開始元素、中間元素和末尾元素。

按照所選取的基準點的順序可以構造一顆順序二叉樹。二叉樹的深度就是遞歸的深度。二叉樹的平衡性越好,算法的性能越好。

2、優化不必要的交換

將交換改爲賦值。

3、優化小數組時的排序方案

大數據排序時對快速排序算法性能的影響很小。但是,只有幾個數據要排序時有效率更高的排序算法。例如,直接插入排序。直接插入排序是簡單排序中性能最好的。

4、優化遞歸操作

遞歸對於算法的效率有着很大的影響。快速排序算法有兩次遞歸操作,對我們來說太奢侈了,我們應該減少遞歸的調用,就可以最大程度的提高性能。我們可以利用尾遞歸

什麼是尾遞歸呢?如果一個函數中遞歸形式的調用出現在函數的末尾,我們稱這個遞歸函數是尾遞歸的。

當編譯器檢測到一個函數調用是尾遞歸的時候,它就覆蓋當前的活躍記錄而不是在棧中去創建一個新的。編譯器可以做到這點,因爲遞歸調用是當前活躍期內最後一條待執行的語句,於是當這個調用返回時棧幀中並沒有其他事情可做,因此也就沒有保存棧幀的必要了。通過覆蓋當前的棧幀而不是在其上重新添加一個,這樣所使用的棧空間就大大縮減了,這使得實際的運行玫率會變得更高。

因此,只要有可能我就需要將遞歸函數寫成尾遞歸的形式。

5、發揮尾遞歸的優勢:處理小部分的的遞歸

對數據規模較小的部分遞歸,對數據規模較大的部分通過尾遞歸的調用分小。儘量把棧的空間利用起來。

具體優化代碼

public class QuickSort01 {

    public static int MAX_LENGTH_INSERT_SORT = 3;

    public static void main(String[] args) {
        int[] array = {5, 9, 1, 9, 5, 3, 7, 6, 1}; // 待排序數組
        sort(array, 0, array.length - 1);
        print(array);
    }

    /**
     * 快速排序
     *
     * @param array 待排序的數組
     * @param low   數組的起始地址
     * @param high  數組的結束地址
     */
    public static void sort(int array[], int low, int high) {
        // 直到兩個下標相遇,程序結束
        // ------------優化點3:小規模排序使用直接插入排序------------
        if (high - low > MAX_LENGTH_INSERT_SORT) {
            // ------------優化點4:改爲尾遞歸------------
            while (low < high) {
                // 查找 k 的位置
                int k = partition(array, low, high);
                // ------------優化點5:處理小部分的的遞歸------------
                if (k - low < high - k) {
                    sort(array, low, k - 1);
                    low = k + 1;
                } else {
                    sort(array, k + 1, high);
                    high = k - 1;
                }
            }
        } else {
            insertSort(array, low, high);
        }
    }

    /**
     * 快速排序,分割的過程
     *
     * @param array 待排序的數組
     * @param low   數組的起始地址
     * @param high  數組的結束地址
     * @return k 值
     */
    public static int partition(int array[], int low, int high) {

        // ------------優化點1:優化選取基準點------------
        selectPoint(array, low, high);

        // 基準點
        int point = array[low];
        // 直到兩個下標相遇,程序結束
        while (low < high) {
            // high 向左移動,直至遇到比point值小的記錄,停止移動
            while (low < high && array[high] >= point) {
                high--;
            }
            // ------------優化點2:將交換改爲賦值------------
            array[low] = array[high];

            //low 向右移動,直至遇到比point值大的記錄,停止移動
            while (low < high && array[low] <= point) {
                low++;
            }
            // ------------優化點2:將交換改爲賦值------------
            array[high] = array[low];
        }

        // ------------優化點2:別忘記將 point 填回去------------
        array[low] = point;

        return low;
    }

    /**
     * 優化選取基準點:選取相應區間的開始元素、中間元素和末尾元素。將值大小處在中間的元素放到low位置。
     * @param array 待排序的數組
     * @param low  數組區間的開始位置
     * @param high 數組區間的結束位置
     */
    public static void selectPoint(int[] array, int low, int high) {
        int mid = low + (high - low)/2;
        if (array[low] > array[high]) {
            swap(array, low, high);
        }
        if (array[mid] > array[high]) {
            swap(array, mid, high);
        }
        // 經過前面兩步,最大值已經在 high 位置
        // 下一步將值大小處在中間的元素放到low位置
        if (array[mid] > array[low]) {
            swap(array, mid, low);
        }
    }

    public static void insertSort(int array[], int low, int high) {
        // 注意:low是數組區間的開始,high是數組區間的結束
        // 循環待排序的部分的數列
        // 第一個數據(下標爲low的數據)由於插入排序剛開始,有序表中沒有任何記錄,可以直接添加到有序表中
        for (int i = low + 1; i < high + 1; i++) {
            int temp = array[i];
            int j = i;
            // 如果前面的元素小於temp,則向後移
            for (; j > low && array[j - 1] > temp; j--) {
                array[j] = array[j - 1];
            }
            // 前一個元素(array[j - 1])和後一個元素(array[j])是相同的
            // 在下一輪時,當array[j - 1]小於或等於temp時,將temp插入array[j](即上一輪的array[j - 1])
            array[j] = temp;
        }
    }

    /**
     * 交換數組中兩個元素的位置
     */
    public static void swap(int array[], int low, int high) {
        int temp = array[low];
        array[low] = array[high];
        array[high] = temp;
    }

    /**
     * 打印數組
     */
    public static void print(int array[]) {
        for (int i = 0; i < array.length; i++) {
            System.out.print(array[i] + "   ");
        }
        System.out.println();
    }
}

5 1 3 5 9 6

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章