Top K 問題
在大規模數據處理中,經常會遇到的一類問題:在海量數據中找出出現頻率最好的前k個數,或者從海量數據中找出最大的前k個數,這類問題通常被稱為top K問題
1:如何在100億數據中找到最大的1000個數
最容易想到的就是將數據全排序,但是效率太低了,對於海量數據處理並不合適。
方法一構建堆:
用構建堆。(找1000個最大的數,構建最小堆)找1000個最小的數構建最大堆
我們知道完全二叉樹有幾個非常重要的特性,就是假如該二叉樹中總共有N個節點,那么該二叉樹的深度就是log2N,
對於小頂堆來說移動根元素到 底部或者移動底部元素到根部只需要log2N,相比N來說時間復雜度優化太多了(1億的logN值是26-27的一個浮點數)
具體思路:先從文件中取出1000個元素構建一個最小堆數組(O(log 1000)),然后對剩下的100億-1000個數字m進行遍歷,如果當前元素大於 最小堆的堆頂,
就是K【0】元素,就用m取代k【0】,對新的數組從新構建小根堆。遍歷結束,這個最小堆就是要找的數。
時間復雜度:O((100億 - 1000)log 1000) 就是O((N-M)logM), 空間復雜度M
這個算法優點是性能尚可,空間復雜度低,IO讀取比較頻繁,對系統壓力大。
例題: 劍指offer最小的k個數
這里還有堆排序
方法二:分治法 即大數據里最常用的MapReduce。
a、將100億個數據分為1000個大分區,每個區1000萬個數據
b、每個大分區再細分成100個小分區。總共就有1000*100=10萬個分區
c、計算每個小分區上最大的1000個數
為什么要找出每個分區上最大的1000個數?舉個例子說明,全校高一有100個班,我想找出全校前10名的同學,很傻的辦法就是,把高一100個班的同學成績都取出來,作比較,這個比較數據量太大了。應該很容易想到,班里的第11名,不可能是全校的前10名。也就是說,不是班里的前10名,就不可能是全校的前10名。因此,只需要把每個班里的前10取出來,作比較就行了,這樣比較的數據量就大大地減少了。我們要找的是100億中的最大1000個數,所以每個分區中的第1001個數一定不可能是所有數據中的前1000個。
d、合並每個大分區細分出來的小分區。每個大分區有100個小分區,我們已經找出了每個小分區的前1000個數。將這100個分區的1000*100個數合並,找出每個大分區的前1000個數。
e、合並大分區。我們有1000個大分區,上一步已找出每個大分區的前1000個數。我們將這1000*1000個數合並,找出前1000.這1000個數就是所有數據中最大的1000個數
(a、b、c為map階段,d、e為reduce階段)
方法三:Hash法。
如果這1億個書里面有很多重復的數,先通過Hash法,把這1億個數字去重復,這樣如果重復率很高的話,會減少很大的內存用量,從而縮小運算空間,
然后通過分治法或最小堆法查找最大的10000個數。