★ 基於「比較」操作的內部排序效能大pk
我們首先總結一下《排序結構專題1-4》中的十種方法的效能((n個關鍵字的待排序列)):
排序方法
平均時間
最壞時間
輔助儲存空間
穩定性
直接插入排序
o(n^2)
o(n^2)
o(1)
√
折半插入排序
o(n^2)
o(n^2)
o(1)
√希爾排序
o(n*logn)
o(n*logn)
o(1)
×起泡排序
o(n^2)
o(n^2)
o(1)
√
快速排序
o(n*logn)
o(n^2)
o(logn)
×簡單選擇排序
o(n^2)
o(n^2)
o(1)
√
樹形選擇排序
o(n*logn)
o(n*logn)
o(n)
√堆排序
o(n*logn)
o(n*logn)
o(1)
×歸併排序
o(n*logn)
o(n*logn)
o(n)
√
1、 o(n^2)級別的普通排序演算法,我們用c++ 的隨機函式rand() 產生的隨機數進行排序,並計算耗費時間。
其中分別隨機生成1w,3w,5w... 19w(增量為2w)共十組待排序列進行測試。得到直接插入排序、折半插入排序、起泡排序、簡單選擇排序的耗時統計圖如下所示(spss軟體做圖統計)。
從上圖可以發現,起泡排序的耗時最大,其他三者的耗時差不多。其中折半插入排序在待排資料量達到19w以後,其效能要比直接插入排序,和簡單排序要好一些。另外,在資料量較小的情況下,插入排序的效能要比選擇排序要略好。
普通演算法分析:在資料規模較小時(9w之內),折半插入、直接插入、簡單選擇插入差不多。當資料量較大時,折半插入要好一些。而起泡排序演算法的時間代價是最昂貴的。
另外,普通排序演算法基本上都是相鄰元素進行比較,因此o(n^2)基本的排序演算法都是穩定的。
2、o(n*logn)級別的先進排序演算法,其時間複雜度要比普通演算法要快得多。由於資料本身要小的多,因此我們沒有拿它們和普通演算法進行比較,而是另外選擇從10w——140w(增量10w)的15組資料進行測試,耗時效能比較如下(spss軟體做圖統計):
從上圖可以發現,先進排序的耗時代價遠遠小於普通排序演算法。而先進演算法之間也有區別。其中快速排序無疑是最優秀的。其次是歸併排序和希爾排序,堆排序稍微差一些,而最差的就是樹形選擇排序了。
先進演算法分析:
(1) 就時間效能而言,
希爾排序、快速排序、樹形選擇排序、堆排序和歸併排序都是較為先進的排序方法。耗時遠小於o(n^2)級別的演算法。
(2) 先進演算法之中,快排的效率是最高的。
但其缺點十分明顯:在待排序列基本有序的情況下,會蛻化成起泡排序,時間複雜度接近 o(n^2)。
(3) 希爾排序的效能讓人有點意外,這種增量插入排序的高效性完全說明了:在基本有序序列中,直接插入排序絕對能達到令人吃驚的效率。但是希爾排序對增量的選擇標準依然沒有較為滿意的答案,要知道增量的選取直接影響排序的效率。
(4) 歸併排序的效率非常不錯,在資料規模較大的情況下,它比希爾排序和堆排序都要好。
(5)堆排序在資料規模較小的情況下還是表現不錯的,但是隨著規模的增大,時間代價也開始和上面兩種排序拉開的距離。
(6)樹形選擇排序並不是較好的先進排序方法,資料規模越大,其耗時代價越高。而且它所需要的額外輔助空間較多,達到o(n)級別。想想看,排序140w資料,需要額外再開闢140w的空間,實在是無法忍受。
(7) 多數先進排序都因為跳躍式的比較,降低了比較次數,但是也犧牲了排序的穩定性。
總的來說,並不存在「最佳」的排序演算法。必須針對待排序列自身的特點來選擇「良好」的演算法。下面有一些指導性的意見:
(1) 資料規模很小,而且待排序列基本有序的情況下,選擇直接插入排序絕對是上策。不要小看它o(n^2)級別。
(2) 資料規模不是很大,完全可以使用記憶體空間。而且待排序列雜亂無序(越亂越開心),快排永遠是不錯的選擇,當然付出log(n)的額外空間是值得的。
(3) 海量級別的資料,必須按塊存放在外存(磁碟)中。此時的歸併排序是乙個比較優秀的演算法。
附:以上兩個圖的資料測試在pentium 4 cpu 3.06ghz下,cpu佔用率0%的情況下執行的結果。
★ 乙個關於o(n*logn)耗時下限的理論
這裡有乙個疑問:是不是o(n*logn)是排序演算法時間代價最好的極限呢?
當然不是,但是如果排序演算法是基於"關鍵字比較"操作的,那麼在最壞情況下確實能夠到達的最好效果就是o(n*logn)了。 在最好情況下就沒必要說了,如果待排序列基本有序,那麼直接插入排序的比較次數都非常的少。
下面我們來證明一下(注意:這些排序演算法的基本操作就是比較,其時間主要消耗在比較次數上)。現在有三個關鍵字k1、k2、k3。那麼下圖給出了這三個關鍵字記錄在任何可能的排序狀態下的判定樹,樹中的內部結點都進行了一次必要的比較。
三個關鍵字的待排序列只有上面葉子結點所描述的6中排序狀態。而判定樹上的每一次比較都是必須的。因此、這個判定樹足以描述通過「比較」進行的排序過程。並且,每乙個待排序列經過排序達到有序序列所需要進行的"比較"次數,恰為從樹根到葉子結點的路徑長度。因此3個關鍵字的比較最少需要2次,最多需要3次。
擴充套件一下,有n個關鍵字序列。那麼就有n!種排序狀態,自然判定樹就有n!個葉子節點。我們知道,二叉樹的樹高為h的情況下,葉子結點最多有2^(h-1)個。而現在又n!個葉子結點,那麼樹高至少為log(n!)+1。也就是說,描述n個記錄排序的判定樹必存在一條長度為[log(n!)+1]的路徑。根據斯特林公式(n!的高精度近似求解公式): log(n!)=n*log(n)。因此,最少的比較次數也就是n*log(n)了。
基於比較操作的排序演算法的時間複雜度下限確實是o(n*logn)。那麼如果不比較呢,耗時代價會不會進一步減少。當然,關於這方面的排序演算法,請見《桶排序 》、《基數排序 》。
基於比較的內部排序總結
基於 比較 操作的內部排序效能大pk 我們首先總結一下 排序結構專題1 4 中的十種方法的效能 n個關鍵字的待排序列 排序方法 平均時間 最壞時間 輔助儲存空間 穩定性 直接插入排序 o n 2 o n 2 o 1 折半插入排序 o n 2 o n 2 o 1 希爾排序 o n logn o n l...
基於比較的排序
1 插入排序 折半插排 減治演算法排序 每次從無序區間選擇第乙個數,插入到有序區間的合適位置 2 3 4 5 9 1768 1 2 3 4 5 9 768 1 2 3 4 5 7 9 68 一共需要多少次插入 size 1 想清楚有序區間 無序區間 插入過程 每次把無序區間的第乙個數進行插入 在有序...
內部非比較排序 計數排序
計數排序是一種演算法複雜度 o n 的排序方法,適合於小範圍集合的排序。比如 100萬學生參加高考,我們想對這100萬學生的數學成績 假設分數為0到100 做個排序。我們如何設計乙個最高效的排序演算法。本文不光給出計數排序演算法的傳統寫法,還將一步步深入討論演算法的優化,直到時間複雜度和空間複雜度最...