top K問題
問題描述
在大規模資料處理中,經常會遇到的一類問題就是在海量資料中找出出現頻率最高的前K個數,或者從海量資料中找出最大的前K個數,這類問題通常被稱為top K問題。
問題解答
針對top K類問題,通常比較好的方案是分治+Trie樹/hash+小頂堆,即先將資料集按照hash方法分解成多個小資料集,然後使用Trie樹或者hash統計每個小資料集中的query詞頻,之後用小頂堆求出每個資料集中出現頻率最高的前K個數,最後在所有top K中求出最終的top K。
例如:有1億個浮點數,如何找出其中最大的10000個?
第一種是將資料全部排序,然後在排序後的集合中進行查詢,最快的排序演算法的時間複雜度一般為O(nlogn),如快速排序。但是在32位的機器上,每個float型別佔4個位元組,1億個浮點數就要佔用400MB的儲存空間,對於一些可用記憶體小於400M的計算機而言,很顯然是不能一次將全部資料讀入記憶體進行排序的。其實即使記憶體能夠滿足要求(我機器記憶體都是8GB),該方法也並不高效,因為題目的目的是尋找出最大的10000個數即可,而排序卻是將所有的元素都排序了,做了很多的無用功。
第二種方法為區域性淘汰法,該方法與排序方法類似,用一個容器儲存前10000個數,然後將剩餘的所有數字——與容器內的最小數字相比,如果所有後續的元素都比容器內的10000個數還小,那麼容器內這個10000個數就是最大10000個數。如果某一後續元素比容器內最小數字大,則刪掉容器內最小元素,並將該元素插入容器,最後遍歷完這1億個數,得到的結果容器中儲存的數即為最終結果 了。此時的時間複雜度為O(n+m^2),其中m為容器的大小,即10000。
第三種方法是分治法,將1億個資料分成100份,每份100萬個資料,找到每份資料中最大的10000個,最後在剩下的10010000個數據裡面找出最大的10000個。如果100萬資料選擇足夠理想,那麼可以過濾掉1億資料裡面99%的資料。100萬個資料裡面查詢最大的10000個數據的方法如下:用快速排序的方法,將資料分為2堆,如果大的那堆個數N大於10000個,繼續對大堆快速排序一次分成2堆,如果大的那堆個數N大於10000個,繼續對大堆快速排序一次分成2堆,如果大堆個數N小於10000個,就在小的那堆裡面快速排序一次,找第10000-n大的數字;遞迴以上過程,就可以找到第1w大的數。參考上面的找出第1w大數字,就可以類似的方法找到前10000大數字了。此種方法需要每次的記憶體空間為10^6
第四種方法是Hash法。如果這1億個書裡面有很多重複的數,先通過Hash法,把這1億個數字去重複,這樣如果重複率很高的話,會減少很大的記憶體用量,從而縮小運算空間,然後通過分治法或最小堆法查詢最大的10000個數。
第五種方法採用最小堆。首先讀入前10000個數來建立大小為10000的最小堆,建堆的時間複雜度為O(mlogm)(m為陣列的大小即為10000),然後遍歷後續的數字,並於堆頂(最小)數字進行比較。如果比最小的數小,則繼續讀取後續數字;如果比堆頂數字大,則替換堆頂元素並重新調整堆為最小堆。整個過程直至1億個數全部遍歷完為止。然後按照中序遍歷的方式輸出當前堆中的所有10000個數字。該演算法的時間複雜度為O(nmlogm),空間複雜度是10000(常數)。
實際上,最優的解決方案應該是最符合實際設計需求的方案,在實際應用中,可能有足夠大的記憶體,那麼直接將資料扔到記憶體中一次性解決即可,也可能機器有多個核,這樣可以採用多執行緒處理整個資料集。
面經:在10億的商品日誌中找出出現最多的一百個商品,請問這個怎麼實現?
對10億的商品日誌求hash值,hash值對1024求餘,將商品分到1024個檔案中,在每個檔案中利用長度100的大頂堆找到前100的商品,再將1024個檔案找出的1024*100的商品用大頂堆遍歷找到前100個