對於海量資料「尋找前k大」,目前總結起來我想一般也就兩種方案:堆排序、k選擇。
這裡需要說明兩點:
(1)如果資料量比較大,一般我們認為k>2,如果k<2,遍歷一遍即可求,無需過多討論
(2)如果資料量不是太大,完全可以做k次冒泡來求得
這裡我們考慮的是資料量比較大,k>2的情況
網上對於解決海量前k大問題比較常用的方法就是堆排。我們知道,使用堆排序是要求堆可以放入記憶體啊,這麼大的資料,怎麼放?這裡有乙個技巧,記憶體中並不是放入所有資料的堆,而是放入k大小的堆,我們知道堆調整的複雜度為lgk,使用k大小的堆,檔案遍歷一遍我們就可以求出前k大,所以複雜度是nlgk,n是詞的個數,只不過這個「遍歷」是檔案操作,係數要比在記憶體中「遍歷」的係數要大一些。
方案:將統計好的詞頻取k個放入記憶體,調整為最小堆,注意這裡是最小堆,求前k大,比較當前的元素與最小堆當中的最小元素,如果它大於最小元素則替換那個最小元素,這樣最後得到的k個元素就是最大的k個。反之,求前k小,用的自然就是最大堆啦。
k選擇是用於求第k大的元素,其實「求前k大」等價於「求第k大」,對資料執行一次k選擇演算法,那麼前k大的元素不就都有了麼。但是問題是k選擇也需要放入記憶體啊,這麼大的資料又放不下了,腫麼辦?是的,又是歸併的思想。不過這裡為大家提供兩種可行的方案,方案1使用歸併的思想,方案2是直接用k選擇的外排序,幾乎不需要記憶體,此為大雄原創,o(∩_∩)o~。
方案1:我當時的想法是,將統計好的詞頻分段,先在記憶體中求出每段的前k大,然後各段的前k大有了,再將這幾段的前k大放入記憶體求得最終的前k大
方案2:直接使用檔案執行前k大演算法。思路是開兩個檔案,選取軸數後乙個檔案放入大數,乙個檔案放小數,同時記錄各檔案放入的數目,從而直接用檔案得到最終的前k大。這樣做只是時間上耗費可能要多一些
這裡對兩種方法做乙個簡單的分析:複雜度。
前k堆方法的複雜度為nlgk,n為詞總數。
k選擇方法複雜度:n + 1/2 + 1/4 + … = 2n. (類似快排:快排是n + n + n + … 共lgn個),這樣看起來貌似前k快一點,呵呵,實際上還要考慮係數的問題,例如操作檔案比操作記憶體的係數必然要大,一般來講:只能說k很小的時候,堆比較快;k很大的時候,前k比較快吧。
海量資料處理專題(五) 堆
什麼是堆 概念 堆是一種特殊的二叉樹,具備以下兩種性質 1 每個節點的值都大於 或者都小於,稱為最小堆 其子節點的值 2 樹是完全平衡的,並且最後一層的樹葉都在最左邊 這樣就定義了乙個最大堆。如下圖用乙個陣列來表示堆 那麼下面介紹二叉堆 二叉堆是一種完全二叉樹,其任意子樹的左右節點 如果有的話 的鍵...
海量資料處理專題(五) 堆
什麼是堆 概念 堆是一種特殊的二叉樹,具備以下兩種性質 1 每個節點的值都大於 或者都小於,稱為最小堆 其子節點的值 2 樹是完全平衡的,並且最後一層的樹葉都在最左邊 這樣就定義了乙個最大堆。如下圖用乙個陣列來表示堆 那麼下面介紹二叉堆 二叉堆是一種完全二叉樹,其任意子樹的左右節點 如果有的話 的鍵...
海量資料處理專題(五) 堆
什麼是堆 概念 堆是一種特殊的二叉樹,具備以下兩種性質 1 每個節點的值都大於 或者都小於,稱為最小堆 其子節點的值 2 樹是完全平衡的,並且最後一層的樹葉都在最左邊 這樣就定義了乙個最大堆。如下圖用乙個陣列來表示堆 那麼下面介紹二叉堆 二叉堆是一種完全二叉樹,其任意子樹的左右節點 如果有的話 的鍵...