如何從海量數據中找出高頻詞?
大家好,我是陌溪
今天分享一道面試常考的海量數據面試題。
題目描述
假如有一個 1G 大小的文件,文件裏每一行是一個詞,每個詞的大小不超過 16byte,要求返回出現頻率最高的 100 個詞。內存大小限制是 10M
解法 1
由於內存限制,我們無法直接將大文件的所有詞一次性讀到內存中。
可以使用分治策略,把一個大文件分解成多個小文件,保證每個文件的大小小於 10M,進而直接將單個小文件讀取到內存中處理。
第一步,首先遍歷大文件,對遍歷到的每個詞 x,執行 hash(x) % 500
,將結果爲 i 的詞存放到文件 f(i) 中,遍歷結束後,可以得到 500 個小文件,每個小文件的大小爲 2M 左右;
第二步,接着統計每個小文件中出現頻數最高的 100 個詞。可以使用 HashMap 來實現,其中 key 是詞,value 是該詞出現的頻率。
對於遍歷到的詞 x,如果在 map 中不存在,則執行 map.put(x, 1)。
若存在,則執行 map.put(x, map.get(x)+1)
,將該詞出現的次數加 1。
第三步,在第二步中找出了每個文件出現頻率最高的 100 個詞之後,通過維護一個小頂堆來找出所有小文件中出現頻率最高的 100 詞。
具體方法是,遍歷第一個文件,把第一個文件中出現頻率最高的 100 個詞構造成小頂堆。
如果第一個文件中詞的個數小於 100,可以繼續遍歷第二個文件,直到構造好有 100 個結點的小頂堆爲止。
繼續遍歷其他小文件,如果遍歷到的詞的出現次數大於堆頂上詞的出現次數,可以用新遍歷到的詞替換堆頂的詞,然後重新調整此堆爲小頂堆。
當遍歷完所有小文件後,這個小頂堆中的詞就是出現頻率最高的 100 詞。
總結一下,這種解法的主要思路如下:
-
採用分治的思想,進行哈希取餘
-
使用 HashMap 統計每個小文件單詞出現的頻次
-
使用小頂堆,遍歷步驟 2 中的小文件,找出詞頻 top100 的詞
但是很容易可以發現問題,在第二步中,如果這個 1G 的大文件中有某個詞詞頻過高,可能導致小文件大小超過 10m。這種情況下該怎麼處理呢?
接下來看另外一種解法。
解法 2
第一步:使用多路歸併排序對大文件進行排序,這樣相同的單詞肯定是緊挨着的
多路歸併排序對大文件進行排序的步驟如下:
① 將文件按照順序切分成大小不超過 2m 的小文件,總共 500 個小文件
② 使用 10MB 內存分別對 500 個小文件中的單詞進行排序
③ 使用一個大小爲 500 大小的堆,對 500 個小文件進行多路排序,結果寫到一個大文件中
其中第三步,對 500 個小文件進行多路排序的思路如下:
-
初始化一個小頂堆,大小就是有序小文件的個數 500。堆中的每個節點存放每個有序小文件對應的輸入流。
-
按照每個有序文件中的下一行數據對所有文件輸入流進行排序,單詞小的輸入文件流放在堆頂。
-
拿出堆頂的輸入流,並其下一行數據寫入到最終排序的文件中,如果拿出來的輸入流中還有數據的話,那麼將這個輸入流再一次添加到棧中。否則說明該文件輸入流中沒有數據了,那麼可以關閉這個流。
-
循環這個過程,直到所有文件輸入流都沒有數據爲止。
第二步:
① 初始化一個 100 個節點的小頂堆,用於保存 100 個出現頻率最多的單詞
② 遍歷整個文件,一個單詞一個單詞的從文件中取出來,並計數
③ 等到遍歷的單詞和上一個單詞不同的話,那麼上一個單詞及其頻率如果大於堆頂的詞的頻率,那麼放在堆中,否則不放
最終,小頂堆中就是出現頻率前 100 的單詞了。
解法 2 相對解法 1,更加嚴謹,如果某個詞詞頻過高或者整個文件都是同一個詞的話,解法 1 不適用。
想與我交流的話,可以到公衆號後臺獲取我的個人微信號~
程序員大彬 非科班轉碼,校招拿了京東、攜程等多家互聯網大廠 offer,專注分享 Java 技術乾貨
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/zj4Tc5xFPNV6K9fdsPdQcw