BitMap算法的核心思想是用bit數組來記錄0-1兩種狀態,然后再將具體數據映射到這個比特數組的具體位置,這個比特位設置成0表示數據不存在,設置成1表示數據存在。
BitMap算在在大量數據查詢、去重等應用場景中使用的比較多,這個算法具有比較高的空間利用率。
本文參考:漫畫:BitMap算法
1. 位圖算法的簡單原理
- 給定長度是10的bitmap,每一個bit位分別對應着從0到9的10個整型數。此時bitmap的所有位都是0。
- 把整型數4存入bitmap,對應存儲的位置就是下標為4的位置,將此bit置為1。
- 把整型數2存入bitmap,對應存儲的位置就是下標為2的位置,將此bit置為1。
要問此時bitmap里存儲了哪些元素?就一目了然。
Bitmap不僅方便查詢,還可以去除掉重復的整型數。
2. BitMap的開源實現
BitMap算法的開源實現由JDK的BitSet和谷歌的EWAHCompressedBitmap。
BitSet是對BitMap算法的簡單實現,而EWAHCompressedBitmap對BitMap的存儲空間做了優化。
我們還是接着上面列子往下講。上面我們已經在BitMap中插入了2和4兩個數,現在數據中的數存儲如下:
加入現在要插入一個非常大的數,比如10000000,那么BitMap必須要開啟一大塊空間來存儲10000000,但是這篇空間中的很多Bit位是用不到的。在這種數據分布極度不均勻的情況下BitMap的空間利用率是很低的。EWAHCompressedBitmap實現就對這種情況作了優化。具體的優化算法這邊就不做詳細解釋了。可以參考這篇博客
3. 使用案列
- 給定10億個不重復的正int的整數,沒排過序的,然后再給一個數,如何快速判斷這個數是否在那10億個數當中。
解法:遍歷40個億數字,映射到BitMap中,然后對於給出的數,直接判斷指定的位上存在不存在即可。
- 使用位圖法判斷正整形數組是否存在重復
解法:遍歷一遍,存在之后設置成1,每次放之前先判斷是否存在,如果存在,就代表該元素重復。
- 使用位圖法進行元素不重復的正整形數組排序
解法:遍歷一遍,設置狀態1,然后再次遍歷,對狀態等於1的進行輸出,參考計數排序的原理。
- 在2.5億個整數中找出不重復的正整數,注,內存不足以容納這2.5億個整數
解法1:采用2-Bitmap(每個數分配2bit,00表示不存在,01表示出現一次,10表示多次,11無意義)。
解法2:采用兩個BitMap,即第一個Bitmap存儲的是整數是否出現,接着,在之后的遍歷先判斷第一個BitMap里面是否出現過,如果出現就設置第二個BitMap對應的位置也為1,最后遍歷BitMap,僅僅在一個BitMap中出現過的元素,就是不重復的整數。
解法3:分治+Hash取模,拆分成多個小文件,然后一個個文件讀取,直到內存裝的下,然后采用Hash+Count的方式判斷即可。
該類問題的變形問題,如已知某個文件內包含一些電話號碼,每個號碼為8位數字,統計不同號碼的個數。8位最多99 999 999,大概需要99m個bit,大概10幾m字節的內存即可。 (可以理解為從0-99 999 999的數字,每個數字對應一個Bit位,所以只需要99M個Bit==12MBytes,這樣,就用了小小的12M左右的內存表示了所有的8位數的電話)
BitMap的一些缺點:
(1)數據碰撞。比如將字符串映射到 BitMap 的時候會有碰撞的問題,那就可以考慮用 Bloom Filter 來解決,Bloom Filter 使用多個 Hash 函數來減少沖突的概率。
(2)數據稀疏。又比如要存入(10,8887983,93452134)這三個數據,我們需要建立一個 99999999 長度的 BitMap ,但是實際上只存了3個數據,這時候就有很大的空間浪費,碰到這種問題的話,可以通過引入 Roaring BitMap 來解決。