排序:快速排序
簡介
快速排序是對氣泡排序的一種改進。它的基本思想是:通過一趟排序將要排序的資料分割成獨立的兩部分,其中一部分的所有資料都比另外一部分的所有資料都要小,然後再按此方法對這兩部分資料分別進行快速排序,整個排序過程可以遞迴進行,以此達到整個資料變成有序序列。
排序原理:
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元素的前面,可以說這個該演算法是穩定的。
常見排序演算法的穩定性:
穩定:
- 氣泡排序
- 插入排序
- 歸併排序
不穩定:
- 選擇排序
- 希爾排序
- 快速排序