天天看點

bitmap計數,求TopK最快的方法?

TopK到底怎麼答?

》介紹了TopK的四種解法,其中随機選擇 (randomized select) 最為經典,用減治法 (Reduce & Conquer) 的思想,将資料規模急速降低,總體複雜度為O(n)。

結尾挖了一個坑:求TopK,有沒有比随機選擇更快的方法呢?

空間換時間,是算法優化中最常見的手段,如果有相對充裕的記憶體,可以有更快的算法。

畫外音:即使記憶體不夠,也可以水準切分,使用分段的方法來操作,減少每次記憶體使用量。

TopK問題描述

從arr[1, 12]={5,3,7,1,8,2,9,4,7,2,6,6} 這n=12個數中,找出最大的k=5個。

比特位圖(bitmap)法

bitmap,是空間換時間的典型代表。它是一種,用若幹個bit來表示集合的資料結構。

例如,集合S={1,3,5,7,9},容易發現,S中所有元素都在1-16之間,于是,可以用16個bit來表示這個集合:存在于集合中的元素,對應bit置1,否則置0。

畫外音:究竟需要多少存存儲空間,取決于集合中元素的值域,在什麼範圍之内。

上述集合S,可以用1010101010000000這樣一個16bit的bitmap來表示,其中,第1, 3, 5, 7, 9個bit位置是1。

假設TopK的n個元素都是int,且元素之間沒有重複,隻需要申請2^32個bit,即4G的記憶體,就能夠用bitmap表示這n元素。

掃描一次所有n個元素,以生成bitmap,其時間複雜度是O(n)。生成後,取TopK隻需要找到最高位的k個bit即可。算法總時間複雜度也是O(n)。

僞代碼為:

bitmap[4G] = make_bitmap(arr[1, n]);
return bitmap[top k bits];           

bitmap算法有個缺點,如果集合元素有重複,相同的元素會被去重,假設集合S中有5個1,最終S制作成bitmap後,這5個1隻對應1個bit位,相當于4個元素被丢掉了,這樣會導緻,找到的TopK不準。該怎麼優化呢?

比特位圖計數

優化方法是,每個元素的1個bit變成1個計數。

bitmap計數,求TopK最快的方法?

如上圖所示,TopK的集合經過比特位圖計數處理後,會記錄每個bit對應在集合S中出現過多少次。

接下來,找TopK的過程,就是bitmap從高位的計數開始,往低位的計數掃描,得到count之和等于k,對應的bit就是TopK所求。

bitmap計數,求TopK最快的方法?

如上圖所示,k=5:

(1)第一個非0的count是1,對應的bit是9;

(2)第二個非0的count也是1,對應的bit是8;

(3)第三個非0的count是2,對應的bit是7;

(4)第四個非0的count是2,對應的bit是6,但TopK隻缺1個數字了,故隻有1個6入選;

故,最終的TopK={9, 8, 7, 7, 6}。

結論:通過比特位圖精準計數的方式,求解TopK,算法整體隻需要不到2次掃描,時間複雜度為O(n),比減治法的随機選擇會更快。

為了鞏固今天的内容,例行挖個坑。

面試中,還有個問題問得比較多:求一個正整數的二進制表示包含多少個1?

例如:7的二進制表示是111,即7的二進制表示包含3個1。

畫外音:我面試過程中從不問這個問題。

最常見的解法是:

uint32_t count_one(uint32_t n){
    uint32_t count=0;
    while(n){
        count ++;
        n &= (n-1);
    }
    return count;
}           

提問:還有多少種更快的方法呢?

原文釋出時間為:2018-09-25

本文作者:58沈劍

本文來自雲栖社群合作夥伴“

架構師之路

”,了解相關資訊可以關注“

繼續閱讀