1. 程式人生 > 其它 >排序:快速排序

排序:快速排序

簡介

快速排序是對氣泡排序的一種改進。它的基本思想是:通過一趟排序將要排序的資料分割成獨立的兩部分,其中一部分的所有資料都比另外一部分的所有資料都要小,然後再按此方法對這兩部分資料分別進行快速排序,整個排序過程可以遞迴進行,以此達到整個資料變成有序序列。

排序原理:

1.首先設定一個分界值,通過該分界值將陣列分成左右兩部分;

2.將大於或等於分界值的資料放到到陣列右邊,小於分界值的資料放到陣列的左邊。此時左邊部分中各元素都小於 或等於分界值,而右邊部分中各元素都大於或等於分界值;

3.然後,左邊和右邊的資料可以獨立排序。對於左側的陣列資料,又可以取一個分界值,將該部分資料分成左右兩 部分,同樣在左邊放置較小值,右邊放置較大值。右側的陣列資料也可以做類似處理。

4.重複上述過程,可以看出,這是一個遞迴定義。通過遞迴將左側部分排好序後,再遞迴排好右側部分的順序。當 左側和右側兩個部分的資料排完序後,整個陣列的排序也就完成了。

切分原理: 把一個數組切分成兩個子陣列的基本思想:

1.找一個基準值,用兩個指標分別指向陣列的頭部和尾部;

2.先從尾部向頭部開始搜尋一個比基準值小的元素,搜尋到即停止,並記錄指標的位置;

3.再從頭部向尾部開始搜尋一個比基準值大的元素,搜尋到即停止,並記錄指標的位置;

4.交換當前左邊指標位置和右邊指標位置的元素;

5.重複2,3,4步驟,直到左邊指標的值大於右邊指標的值停止。

程式碼實現:

/**
 * 快速排序
 * @author wen.jie
 * @date 2021/8/6 9:15
 */
public class Quick extends AbstractSort{

    public static void sort(Comparable[] a){
        sort(a, 0, a.length-1);
    }

    public static void sort(Comparable[] a, int lo, int hi){
        if(hi <= lo) return;
        //分組,返回是分組的分界值所在的索引
        int partition = partition(a, lo, hi);
        //左子組有序
        sort(a, lo, partition - 1);
        //右子組有序
        sort(a, partition+1, hi);
    }

    /**
     * 對陣列a中,從索引lo到索引hi之間的元素進行分組,並返回分組界限對應的索引
     * 分界值是變換後的索引
     * @author wen.jie
     * @date 2021/8/6 9:21
     */
    public static int partition(Comparable[] a, int lo, int hi){
        Comparable key = a[lo];
        int left = lo;
        int right = hi+1;
        while (true){

            while (less(key, a[--right])){
                //找到的元素比分界值大
                if (right == lo){
                    break;
                }
            }
            while (less(a[++left], key)){
                //找到的元素比分界值小
                if (left == hi){
                    break;
                }
            }
            if(left >= right) break;
            else exchange(a, left, right);
        }
        exchange(a, lo, right);
        return right;
    }

}

總結

快速排序是另外一種分治的排序演算法,它將一個數組分成兩個子陣列,將兩部分獨立的排序。快速排序和歸併排序是互補的:歸併排序將陣列分成兩個子陣列分別排序,並將有序的子陣列歸併從而將整個陣列排序,而快速排序的方式則是當兩個陣列都有序時,整個陣列自然就有序了。在歸併排序中,一個數組被等分為兩半,歸併呼叫發生在處理整個陣列之前,在快速排序中,切分陣列的位置取決於陣列的內容,遞迴呼叫發生在處理整個陣列之後。

快速排序時間複雜度分析:

快速排序的一次切分從兩頭開始交替搜尋,直到left和right重合,因此,一次切分演算法的時間複雜度為O(n),但整個快速排序的時間複雜度和切分的次數相關。

最優情況:每一次切分選擇的基準數字剛好將當前序列等分。

如果我們把陣列的切分看做是一個樹,那麼上圖就是它的最優情況的圖示,共切分了logn次,所以,最優情況下快 速排序的時間複雜度為O(nlogn);

最壞情況:每一次切分選擇的基準數字是當前序列中最大數或者最小數,這使得每次切分都會有一個子組,那麼總共就得切分n次,所以,最壞情況下,快速排序的時間複雜度為O(n^2);

平均情況:每一次切分選擇的基準數字不是最大值和最小值,也不是中值,這種情況我們也可以用數學歸納法證明,快速排序的時間複雜度為O(nlogn),由於數學歸納法有很多數學相關的知識,容易使我們混亂,所以這裡就不對平均情況的時間複雜度做證明了。

排序的穩定性

穩定性的定義:

陣列arr中有若干元素,其中A元素和B元素相等,並且A元素在B元素前面,如果使用某種排序演算法排序後,能夠保證A元素依然在B元素的前面,可以說這個該演算法是穩定的。

常見排序演算法的穩定性:

穩定:

  • 氣泡排序
  • 插入排序
  • 歸併排序

不穩定:

  • 選擇排序
  • 希爾排序
  • 快速排序