計數排序的缺點很明顯,需要額外的空間C來作為計數數組,雖然時間復雜度為O(n+k),但當輸入序列里元素取值很大的時侯,如k=O(n2),時,此時時間復雜度已經達到n2數量級了,空間的消耗也是讓人無法承受的。這里介紹一種另一種線性排序算法——基數排序,可以應對數值很大的情況。
基數排序,即一個數位一個數位地進行排序,平常生活中我們經常使用的一種算法思想:如要對一個日期進行排序,日期中由年、月、日組成的,對於這個問題,我們經常使用的是先比較年份,如果相同再比較月份,如果還相同就比較日。
同理,我們比較一組數,也可以采取這種思想。例如我們使用這種思想對下面四個數進行排序:123、312、245、531,第一次按百位排序:123、245、312、531;第二次按十位排序:312、123、531、245;第三次按個位數排序:531、312、123、245。咦?為什么最后排出來的結果並不是預期的那樣?原因是我們從高位開始排序,已經差不多整體有序之后,再到低位時,又全部被打亂了。如果我們之后這樣做就不會亂了:高位相同的數,再將它們的低位進行排序….不過這個實現一起比較困難一些。
這里,我們換成從最低有效位到最高有效位進行排序,那么還是上面那個例子:
個位 => 十位 => 百位
531 312 123
312 123 245
123 531 312
245 245 531
可以看到結果正確。通俗地講,之所以先排低位再排高位,是因為越是后排的數位,其對結果次序的影響越大,很顯然是高位比低位對數的大小影響大!
這里再給出一個簡單的證明過程:
假定一組序列低t-1位已經排序好了,現在我們按照第t位進行排序。我們只需證明對於任意兩個數來說,按照第t位排序之后其相對位置正確。任意兩個數這時有兩種情況:
1)它們第t位相同,那么此時如果我們保持它們位置的穩定性,那么它們最后仍然有序;
2)它們第t位不同,按照第t位排序之后就有序了。
所以,即證明了基數排序的正確性。從這里我們可以看出非常關鍵的一點——排序必需要穩定!很快就想到使用前面說過的計數排序算法!
那么可以得出計數排序的偽代碼為:
可以得到時間復雜度為O(d(n+k)); 當d為常數、k=O(n)時,基數排序有線性的運行時間的。
更具體更普遍一點的算法:
/* n為待排序數組A的元素個數,B為按位計數排序后暫存數據的輔助數組,C則是計數數組,b為最大元素的長度(bit位數),r為每個數位的長度(即計數排序以2r進制) */
注:代碼中2r是指2r!
基數排序比較適合對取值很大的數進行排序,也可用來對字符串進行排序。
但基數排序的缺點是不呈現時空局部性,因為在按位對每個數進行排序的過程中,一個數的位置可能發生巨大的變化,所以不能充分利用現代機器緩存提供的優勢。同時計數排序作為中間穩定排序的話,不具有原地排序的特點,當內存容量比較寶貴的時候,還是有待商榷。
源代碼下載地址:
參考文獻:
[1] 算法之道. 鄒恆明. 機械工業出版社,2010.
[2] 算法導論. Cormen ,T.H. 等著,潘金貴等譯. 機械工業出版社,2006.
版權申明:版權所有,翻版不究!歡迎學習交流。如發現BUG,懇請批評指正!
