回顧前面2篇文章我們提到了桶算法和冒泡算法,雖然冒泡算法解決了桶算法的空間問題,但是如果排序的基數比較大,你會發現冒泡算法的時間複雜度O(N²)也是驚人的,有沒有一種更好的算法既能解決空間問題又能解決時間複雜度的問題呢?答案就是我們今天要說的:快速排序算法
先上代碼實現:
public class QuickSort {
public static int[] sort(int[] waitNumbers, int start, int end) {
// 當開始下標大於等於結束下標時,遞歸結束 返回排序後的結果
if (start >= end) {
return waitNumbers;
}
int i = start; //左邊哨兵 初始值
int j = end; //右邊哨兵 初始值
int t; // 臨時變量 交換值用的
int temp = waitNumbers[start]; //存放基準數
//循環執行直到開始下標 大於等於 結束下標時爲止,
//說明此輪排序結束,交換基準值,再次執行排序
while (i < j) {
/**
* 右邊下標先開始掃描 直到掃描到比基準數小的數爲止,
* 思考:爲什麼一定是j先開始掃描?
* 解:因爲我們要滿足比基準數小的在左邊,如果i先開始
* 掃描,會出現i大於基準數的情況,爲了滿足i所在的值
* 一定要小於基準數,我們必須先從右邊開始掃描
* 當j和i相交時,一定是j找到了比基準值小的數,如果
* 是i先開始,可能會當i找到了比基準值大的時候,j還
* 沒找到比基準值小的,這個時候交換的不是i j的值,
* 而是i和基準值的值,此時出現了比基準值大的值跑到
* 基準值左邊去了
*/
while (waitNumbers[j] >= temp) {
j--;
}
// 左邊下標開始往右掃描。直到掃描到比基準數大的數爲止
while (waitNumbers[i] <= temp && i < j) {
i++;
}
// 當i找到比基準數大,j找到比基準數小的時候,
// 交換i和j下標的值 繼續往下執行,直到i>=j
if (i < j) {
t = waitNumbers[i];
waitNumbers[i] = waitNumbers[j];
waitNumbers[j] = t;
}
}
// 交換基準值
waitNumbers[start] = waitNumbers[i];
waitNumbers[i] = temp;
// 遞歸調排序執行基準值左邊的值
sort(waitNumbers, start, i - 1);
// 遞歸調排序執行基準數右邊的值
sort(waitNumbers, i + 1, end);
return waitNumbers;
}
public static void main(String[] args) {
// 待排序的數字
int[] waitNumbers = {8, 15, 3, 13, 6, 35, 45, 2, 10};
QuickSort.sort(waitNumbers, 0, waitNumbers.length - 1);
System.out.println(JSON.toJSONString(waitNumbers));
}
}
結果:[2,3,6,8,10,13,15,35,45]
分析:思路分析已經在代碼中分析很明確,這裏就不再說了,既然這種算法能有效解決冒泡算法時間複雜度的問題,那麼我們就來分析下
快速排序爲什麼比冒泡排序速度要快?
解:我們都知道冒泡排序是每次將相鄰兩個數進行比較和交換,而快速排序是每次設置一個基準點,將小於基準點的數放在左邊,大於基準點的數放右邊,每次交換的就不是相鄰的兩個數了,交換的距離大了,總的交換次數也少了,當然速度也就比冒泡排序快了,這種算法有沒有一種似曾相識的感覺,如果有,那就對了,那就是二分查找思想,當然這不是二分查找,不要混淆。
快速排序算法的時間複雜度?
平均時間複雜度:快速排序算法的時間複雜度是跟原始數據的順序有密切關係的,基準值顯得特別重要了,我們瞭解到快速排序之所以快,是因爲他可以從基準值的左邊兩邊同時遞歸排序下去,所以最理想的狀態就是基準值處於原始無序數據的中間,這樣能最大效率讓兩邊排序,同時減少遞歸劃分出的區域,此時時間複雜度爲O(NlogN),即平均時間複雜度;
最差時間複雜度:還有一種最差的情況就是每次作爲基準值的數剛好是數組中最小/最大的那個數,此時其實就相當於冒泡,每次排好一個元素位置,所以最差時間複雜度等於冒泡算法時間複雜度O(N²)
優點:時間複雜度小,效率高,速度快
缺點:對於越是有序的數組排序效率越低
學習了三種排序算法我們做個簡單的總結:
桶算法:速度最快O(M+N),佔空間最大O(N),適合基數小的正整數排序,
適合先去重後排序的場景
冒泡算法:速度最慢,佔空間最小O(1),最穩當
快速排序算法:速度優於冒泡O(NlogN),佔優於桶O(log2n)~O(n),
穩定性次於冒泡