數(shù)據(jù)結(jié)構(gòu)與算法:桶排序—如何根據(jù)年齡給100萬用戶數(shù)據(jù)排序
一、定義
桶排序是一種線性時間的排序算法。
桶排序需要創(chuàng)建若干個桶來協(xié)助排序。
每一個桶(bucket)代表一個區(qū)間范圍,里面可以承載一個或多個元素。
桶排序的第1步,就是創(chuàng)建這些桶,并確定每一個桶的區(qū)間范圍具體需要建立多少個桶,
如何確定桶的 區(qū)間范圍,有很多種不同的方式。
我們這里創(chuàng)建的桶數(shù)量等于原始數(shù)列的元素數(shù)量,除最后一個桶只包含數(shù)列最大值外, 前面各個桶的區(qū)間按照比例來確定。
區(qū)間跨度 = (最大值-最小值)/ (桶的數(shù)量 - 1)
二、思路
假設(shè)有一個非整數(shù)數(shù)列如下: 4.5,0.84,3.25,2.18,0.5
第2步,遍歷原始數(shù)列,把元素對號入座放入各個桶中。
第3步,對每個桶內(nèi)部的元素分別進行排序(顯然,只有第1個桶需要排序)
第4步,遍歷所有的桶,輸出所有元素: 0.5,0.84,2.18,3.25,4.5
三、代碼實現(xiàn)
四、復(fù)雜度
時間復(fù)雜度:O(n)
空間復(fù)雜度:O(n)
穩(wěn)定性:穩(wěn)定
五、適用場景
桶排序?qū)σ判驍?shù)據(jù)的要求是非??量痰摹?/p>
首先,要排序的數(shù)據(jù)需要很容易就能劃分成m個桶,并且,桶與桶之間有著天然的大小順序。這樣每個桶內(nèi)的數(shù)據(jù)都排序完之后,桶與桶之間的數(shù)據(jù)不需要再進行排序。
其次,數(shù)據(jù)在各個桶之間的分布是比較均勻的。如果數(shù)據(jù)經(jīng)過桶的劃分之后,有些桶里的數(shù)據(jù)非常多,有些非常少,很不平均,那桶內(nèi)數(shù)據(jù)排序的時間復(fù)雜度就不是常量級了。在極端情況下,如果數(shù)據(jù)都被劃分到一個桶里,那就退化為O(nlogn)的排序算法了。
桶排序比較適合用在外部排序中。所謂的外部排序就是數(shù)據(jù)存儲在外部磁盤中,數(shù)據(jù)量比較大,內(nèi)存有限,無法將數(shù)據(jù)全部加載到內(nèi)存中。
六、案例
1、需求
我們有10GB的訂單數(shù)據(jù),我們希望按訂單金額(假設(shè)金額都是正整數(shù))進行排序,但是我們的內(nèi)存有限,只有幾百MB,沒辦法一次性把10GB的數(shù)據(jù)都加載到內(nèi)存中。這個時候該怎么辦呢?
2、桶排序解決方案
我們可以先掃描一遍文件,看訂單金額所處的數(shù)據(jù)范圍。假設(shè)經(jīng)過掃描之后我們得到,訂單金額最小是1元,最大是10萬元。我們將所有訂單根據(jù)金額劃分到100個桶里,第一個桶我們存儲金額在1元到1000元之內(nèi)的訂單,第二桶存儲金額在1001元到2000元之內(nèi)的訂單,以此類推。每一個桶對應(yīng)一個文件,并且按照金額范圍的大小順序編號命名(00,01,02...99)。
理想的情況下,如果訂單金額在1到10萬之間均勻分布,那訂單會被均勻劃分到100個文件中,每個小文件中存儲大約100MB的訂單數(shù)據(jù),我們就可以將這100個小文件依次放到內(nèi)存中,用快排來排序。等所有文件都排好序之后,我們只需要按照文件編號,從小到大依次讀取每個小文件中的訂單數(shù)據(jù),并將其寫入到一個文件中,那這個文件中存儲的就是按照金額從小到大排序的訂單數(shù)據(jù)了。
不過,你可能也發(fā)現(xiàn)了,訂單按照金額在1元到10萬元之間并不一定是均勻分布的 ,所以10GB訂單數(shù)據(jù)是無法均勻地被劃分到100個文件中的。有可能某個金額區(qū)間的數(shù)據(jù)特別多,劃分之后對應(yīng)的文件就會很大,沒法一次性讀入內(nèi)存。這又該怎么辦呢?
針對這些劃分之后還是比較大的文件,我們可以繼續(xù)劃分,比如,訂單金額在1元到1000元之間的比較多,我們就將這個區(qū)間繼續(xù)劃分為10個小區(qū)間,1元到100元,101元到200元,201元到300元....901元到1000元。如果劃分之后,101元到200元之間的訂單還是太多,無法一次性讀入內(nèi)存,那就繼續(xù)再劃分,直到所有的文件都能讀入內(nèi)存為止。?