1. 程式人生 > >資料結構與算法系列11--桶排序、計數排序、基數排序

資料結構與算法系列11--桶排序、計數排序、基數排序

線性排序演算法介紹

1.線性排序演算法包括桶排序、計數排序、基數排序
2.線性排序的演算法的時間複雜度為:O(n)
3.此三種排序演算法都不涉及元素之間的比較操作,是非基於比較的排序演算法。
4.對要排序的資料要求很苛刻,只能適用在某些特殊場景,所以重點掌握此3鍾演算法的適用場景。

桶排序

演算法原理:
將要排序的資料分到幾個有序的桶裡,每個桶裡再單獨進行快速排序。桶內排完序後,再把桶內的資料按照順序依次取出組成一個新的序列,這個新的序列就是有序的了。
使用條件:
1.要排序的資料可以很容易的就能劃分到m個桶裡,並且桶與桶之間有著天然的大小順序。
2.資料在各個桶之間的分佈是比較均勻的。因為如果有些桶裡的資料非常多,有些非常少,很不平均,那桶內資料排序的時間複雜度就不是常量級了。在極端情況下,如果資料都被劃分到一個桶裡,那就退化為O(nlogn)的排序演算法了。
適用場景:


桶排序比較適合在外部排序中,這裡的外部排序是指資料儲存在外部磁碟中,資料量比較大,記憶體有限,無法將資料一次性全部載入到記憶體中。
應用案例:
需求描述:
有10GB的訂單資料,需按訂單金額(假設金額都是正整數)進行排序,但記憶體有限,僅幾百MB
解決思路:
首先,我們先掃描一遍檔案,檢視訂單金額所處的資料範圍。假設我們掃描後得到訂單的資料範圍是1元到10萬元,那麼我們將所有的訂單資料劃分到100個桶裡,第一個桶我們儲存1-1000元之間的資料,第二桶我們儲存1001-2000之間的資料,依次類推,每一個桶對應一個檔案,並按照金額範圍的大小順序命名(00,01,02…99)。然後將100個檔案依次放到記憶體中用快排進行排序。所有檔案排好序後,只需要按照檔案命名序號從小到大依次讀取每個小檔案,並寫到大檔案中即可。
注意:
如果分成的單個小檔案還是大於記憶體使用大小,無法載入到記憶體中,則針對此檔案繼續按照前面的思路進行劃分處理即可。

桶排序的時間複雜度為什麼是O(n)呢?
如果要排序的資料有n個,我們把它們均勻地劃分到m個桶內,每個桶裡就有k=n/m個元素。每個桶內部使用快速排序,時間複雜度為O(klogk)。m個桶排序的時間複雜度就是O(mklogk),因為k=n/m,所以整個桶排序的時間複雜度就是O(nlog(n/m))。當桶的個數m接近資料個數n時,log(n/m)就是一個非常小的常量,這個時候桶排序的時間複雜度接近O(n)。

計算排序

演算法原理:
1.計數其實就是桶排序的一種特殊情況。
2.當要排序的n個數據所處範圍並不大時,比如最大值為k,則分成k個桶
3.每個桶內的資料值都是相同的,就省掉了桶內排序的時間。
使用條件:


1.只能用在資料範圍不大的場景中,若資料範圍k比要排序的資料n大很多,就不適合用計數排序;
2.計數排序只能給非負整數排序,其他型別需要在不改變相對大小情況下,轉換為非負整數;
3.比如如果考試成績精確到小數後一位,就需要將所有分數乘以10,轉換為整數。
應用案例:
如果你所在的省有50萬考生,如何通過成績快速排序得出名次呢?
考生的滿分是900分,最小是0分,這個資料的範圍很小,所以我們可以分成901個桶,對應分數從0分到900分。根據考生的成績,我們將這50萬考生劃分到這901個桶裡。桶內的資料都是分數相同的考生,所以並不需要再進行排序。我們只需要依次掃描每個桶,將桶內的考生依次輸出到一個數組中,就實現了50萬考生的排序。因為只涉及掃描遍歷操作,所以時間複雜度是O(n)。

基數排序

演算法原理:(這裡以排序10萬個手機號為例子來說明)
1.比較兩個手機號碼a,b 的大小,如果在前面幾位中a已經比b大了,那後面幾位就不用看了。
2.藉助穩定排序演算法的思想,可以先按照最後一位來排序手機號碼,然後再按照倒數第二位來重新排序,以此類推,最後按照第一位重新排序。
3.這樣經過11次排序之後,手機號碼就變為有序了。
4.在按位排序時,因為整個資料範圍非常小(即0~9之間的資料),我們可以使用前面的桶排序或者基數排序進行排序,因為他們的時間複雜度是O(n),因為這裡需要11次排序,所以總的時間複雜度是O(11*n),忽略常數值,所以最終的時間複雜度是O(n)。
使用條件:
1.要求資料可以分割成獨立的位來比較。
2.位之間有遞進關係,如果a資料的高位比b資料大,那麼低位就不用比較。
3.每一位的資料範圍不要太大,要可以用線性排序進行排序,否則基數排序的時間複雜度就無法做到O(n)。