1. 程式人生 > >快速排序 Vs. 歸併排序 Vs. 堆排序——誰才是最強的排序演算法

快速排序 Vs. 歸併排序 Vs. 堆排序——誰才是最強的排序演算法

知乎上有一個問題是這樣的:

堆排序是漸進最優的比較排序演算法,達到了O(nlgn)這一下界,而快排有一定的可能性會產生最壞劃分,時間複雜度可能為O(n^2),那為什麼快排在實際使用中通常優於堆排序?

昨天剛好寫了一篇關於快排優化的文章,今天再多做一個比較吧。首先先看一個排序演算法圖:

排序方法平均情況最好情況最壞情況輔助空間穩定性
氣泡排序O(n^2)O(n)O(n^2)O(1)穩定
簡單選擇排序O(n^2)O(n^2)O(n^2)O(1)穩定
直接插入排序O(n^2)O(n)O(n^2)O(1)穩定
希爾排序O(nlogn)~O(n^2)O(n^1.3)O(n^2)O(1)不穩定
堆排序O(nlogn)O(nlogn)O(nlogn)O(1)不穩定
歸併排序O(nlogn)O(nlogn)O(nlogn)O(n)穩定
快速排序O(nlogn)O(nlogn)O(n^2)O(logn)~O(n)不穩定
可以看到,到達nlogn級別的排序演算法,一共有三種,分別是堆排序,歸併排序以及快速排序,其中只有歸併排序最穩定。那麼,為什麼要說快速排序的平均情況是最快的呢?

實際上在演算法分析中,大O的作用是給出一個規模的下界,而不是增長數量的下界。因此,演算法複雜度一樣只是說明隨著資料量的增加,演算法時間代價增長的趨勢相同,並不是執行的時間就一樣,這裡面有很多常量引數的差別,比如在公式裡各個排序演算法的前面都省略了一個c,這個c對於堆排序來說是100,可能對於快速排序來說就是10,但因為是常數級所以不影響大O。

另外,即使是同樣的演算法,不同的人寫的程式碼,不同的應用場景下執行時間也可能差別很大。下面是一個測試資料:

測試的平均排序時間:資料是隨機整數,時間單位是s
資料規模    快速排序       歸併排序        希爾排序        堆排序
1000萬       0.75           1.22          1.77          3.57
5000萬       3.78           6.29          9.48         26.54  
1億          7.65          13.06         18.79         61.31

堆排序每次取一個最大值和堆底部的資料交換,重新篩選堆,把堆頂的X調整到位,有很大可能是依舊調整到堆的底部(堆的底部X顯然是比較小的數,才會在底部),然後再次和堆頂最大值交換,再調整下來,可以說堆排序做了許多無用功。

總結起來就是,快排的最壞時間雖然複雜度高,但是在統計意義上,這種資料出現的概率極小,而堆排序過程裡的交換跟快排過程裡的交換雖然都是常量時間,但是常量時間差很多。