在大規模數據處理中,常常會遇到的一類問題:在海量數據中找出出現頻率最好的前k個數,或者從海量數據中找出最大的前k個數,這類問題一般被稱爲top K問題。例如,在搜索引擎中,統計搜索最熱門的10個查詢詞;在歌曲庫中統計下載最高的前10首歌等。算法
一、最容易想到的方法是將數據所有排序。該方法並不高效,由於題目的目的是尋找出最大的10000個數便可,而排序倒是將全部的元素都排序了,作了不少的無用功。數組
二、局部淘汰法。用一個容器保存前10000個數,而後將剩餘的全部數字一一與容器內的最小數字相比,若是全部後續的元素都比容器內的10000個數還小,那麼容器內這個10000個數就是最大10000個數。若是某一後續元素比容器內最小數字大,則刪掉容器內最小元素,並將該元素插入容器,最後遍歷完這1億個數,獲得的結果容器中保存的數即爲最終結果了。此時的時間複雜度爲O(n+m^2),其中m爲容器的大小。性能
這個容器能夠用(小頂堆)最小堆來實現。咱們知道徹底二叉樹有幾個很是重要的特性,就是假如該二叉樹中總共有N個節點,那麼該二叉樹的深度就是log2N,對於小頂堆來講移動根元素到 底部或者移動底部元素到根部只須要log2N,相比N來講時間複雜度優化太多了(1億的logN值是26-27的一個浮點數)。基本的思路就是先從文件中取出1000個元素構建一個小頂堆數組k,而後依次對剩下的100億-1000個數字進行遍歷m,若是m大於小頂堆的根元素,即k[0],那麼用m取代k[0],對新的數組進行從新構建組成一個新的小頂堆。這個算法的時間複雜度是O((100億-1000)log(1000)),即O((N-M)logM),空間複雜度是M大數據
這個算法優勢是性能尚可,空間複雜度低,IO讀取比較頻繁,對系統壓力大。優化
三、第三種方法是分治法,即大數據裏最經常使用的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個數。
對於海量數據處理,思路基本上是:必須分塊處理,而後再合併起來。