目錄
十大經典排序演算法江山圖
大轉盤抽獎之分桶實現
桶排序
桶排序場景
演算法思想
圖解桶排序
程式碼實現
時間複雜度分析
空間複雜度分析
穩定性分析
適用條件
十大經典排序演算法江山圖
十大經典排序演算法江山圖
十大排序分類
如上圖所示(圖來自於極客時間演算法訓練營超哥的資料),我之前寫的七大排序演算法,都是比較類排序,最後三種是時間複雜度是O(n)的非比較類排序演算法:桶排序、計數排序、基數排序。因為這些排序演算法的時間複雜度是線性的,所以我們把這類排序演算法叫作線性排序(Linear sort)。之所以能做到線性的時間複雜度,主要原因是,這三個演算法是非基於比較的排序演算法,都不涉及元素之間的比較操作。
這幾種排序算法理解起來都不難,時間、空間複雜度分析起來也很簡單,但是對要排序的資料要求很苛刻,所以弄清楚這三種排序的適用場景是重點。
大轉盤抽獎之分桶實現
我想到了我實習負責寫的第一個業務,就是大轉盤抽獎,實現的核心抽獎演算法其實就是用的分桶。
業務場景:一個抽獎活動裡面有很多個獎品,每個獎品的中獎率都不一樣,其中的未中獎也相當於一種獎品,所有獎品中獎率加起來和是1,外表如下所示,想玩玩的朋友可以一鍵到達 http://shop386997。kuaizhan。com/pages/marketing-package/fortune-wheel/fortune-wheel?id=5fd5b484fa079a000618c65a
大轉盤抽獎
例如上圖中,積分獎品,優惠券獎品,贈品獎品三種獎品機率分別為20%,20%,30%,那麼未中獎機率是30%。
我的實現:每次抽獎都生成一個1~100的隨機數,根據每個獎品後臺設定的中獎機率的大小進行分桶,隨機數在1~20代表中獎積分,在20~40內的數代表中獎優惠券,40~70內代表中獎贈品,70~100內的隨機數代表未中獎,這就是抽獎演算法的核心實現,這其實和分桶差不多,將100內的數分為了四個桶。
桶排序
桶排序場景
比如說我們有10GB的訂單資料,我們希望按訂單金額(假設金額都是正整數)進行排序,但是我們的記憶體有限,只有幾百MB,沒辦法一次性把10GB的資料都加 載到記憶體中。這個時候該怎麼辦呢?
我們可以先掃描一遍檔案,看訂單金額所處的資料範圍。假設經過掃描之後我們得到,訂單金額最小是1元,最大是10萬元。我們將所有訂單根據金額劃分到100個桶裡,第一個桶我們儲存金額在1元到1000元之內的訂單,第二桶儲存金額在1001元到2000元之內的訂單,以此類推。每一個桶對應一個檔案,並且按照 金額範圍的大小順序編號命名(00,01,02。。。99)。
理想的情況下,如果訂單金額在1到10萬之間均勻分佈,那訂單會被均勻劃分到100個檔案中,每個小檔案中儲存大約100MB的訂單資料,我們就可以將這100個小 檔案依次放到記憶體中,用快排來排序。等所有檔案都排好序之後,我們只需要按照檔案編號,從小到大依次讀取每個小檔案中的訂單資料,並將其寫入到一個文 件中,那這個檔案中儲存的就是按照金額從小到大排序的訂單資料了。
不過,你可能也發現了,訂單按照金額在1元到10萬元之間並不一定是均勻分佈的 ,所以10GB訂單資料是無法均勻地被劃分到100個檔案中的。有可能某個金額區間的資料特別多,劃分之後對應的檔案就會很大,沒法一次性讀入記憶體。這又該怎麼辦呢?
針對這些劃分之後還是比較大的檔案,我們可以繼續劃分,比如,訂單金額在1元到1000元之間的比較多,我們就將這個區間繼續劃分為10個小區間,1元 到100元,101元到200元,201元到300元。。。901元到1000元。如果劃分之後,101元到200元之間的訂單還是太多,無法一次性讀入記憶體,那就繼續再劃分,直到所有的檔案都能讀入記憶體為止。
演算法思想
桶排序,顧名思義,會用到“桶”,核心思想是將要排序的資料分到幾個有序的桶裡,每個桶裡的資料再單獨進行排序(有可能再使用別的排序演算法或是以遞迴方式 繼續使用桶排序進行排),桶內排完序後,再把每個桶裡的資料按照順序依次取出,組成的序列就是有序的了。
頗有點大事化小,小事化了的感覺
圖解桶排序
圖解桶排序
值得注意的是,桶的個數是人為指定的,不隨著陣列大小和數值大小改變(例如上面的例子中,可以根據檔案大小和記憶體大小,得到桶的個數)。
桶內的資料範圍是 (max-min)/k 因為最後一組的原因向上取整,k是桶的個數,這個地方是(46-4)/5 向上取整得到9。
步驟:
先進行陣列的最大最小值的掃描,得到最值;
計算每個桶的額分割槽範圍;
遍歷原陣列,將每個值放到對應範圍的桶內,按照桶讀取資料就是有序的了;
程式碼實現
這裡假設每個桶的大小為5,程式碼實現如下:
import java。util。ArrayList;import java。util。Collections;/** * @author by zengzhiqin * 2020-12-13 */public class BucketSort { public static int[] bucketSort(int[] arr) { if (arr == null || arr。length < 2) { return arr; } int length = arr。length; double max = arr[0]; double min = arr[0]; // 陣列的最大值與最小值 for (int i = 1; i < arr。length; i++) { if(arr[i] < min) { min = arr[i]; } if(max < arr[i]) { max = arr[i]; } } // 桶的初始化 int bucketNum = 5; // 每個桶內還是陣列 ArrayList 桶排序結果 根據這個圖回去看上面圖解分桶中,桶裡面的資料是不是如此,這裡是先進行了一遍陣列值的大小掃描,實際開發中很多業務場景下,我們自己知道資料的最大最小範圍,例如 時間複雜度分析 假設要排序的資料有n個,均勻地劃分到 k 個桶內。 遍歷所有元素入桶過程,時間複雜度為O(n); 遍歷每個桶,進行排序,如果每個桶內只有一個元素,時間複雜度O(k); 總的為 O(n+k); 實際上,這個還取決於桶內排序演算法,如果每個桶內元素很多,假設使用的桶內快排,實際的時間複雜度要比這個大。 看起來很美好,但是這是建立在美好假設的情況下, 桶排序對排序的資料要求是非常苛刻的 ,下面看下桶排序的資料條件: 資料值比較均勻,在各個桶之間分佈就比較均勻; 能確定資料的範圍,資料的跨度不會特別大; 第一條,如果桶排序之後有些桶資料特別多,有些特別少,那麼資料量多的桶內資料排序時間複雜度就會很高 第二條,資料跨度特別大,容易引起資料分佈不均,例如總共就5個數,0,10000,1000000,10000000,1000000000,這樣就很不好分桶,也就不適合桶排序。 桶排序比較適合用在外部排序中。所謂的外部排序就是資料儲存在外部磁碟中,資料量比較大,記憶體有限,無法將資料全部載入到記憶體中。 空間複雜度分析 O(n+k)。 穩定性分析 穩定。 資料進桶時可以控制相同元素的先後順序保持不變。 適用條件 用於均勻分佈的數字陣列。 往期類似推薦: 極客演算法訓練筆記(八),十大經典排序之堆排序,被樹耽誤的陣列 極客演算法訓練筆記(七),十大經典排序之歸併排序,全網最詳 極客演算法訓練筆記(六),十大經典排序之希爾排序,快速排序 極客演算法訓練筆記(五),十大經典排序之冒泡,選擇,插入排序 歡迎點贊分享在看,關注公眾號《阿甘的碼路》看更多內容,有問題可以加我微信私我指正,各大平臺也會同步只不過排版可能有些會不太一樣~ 參考資料:極客時間演算法訓練營資料,資料結構與演算法之美