bitmap是一種簡單的數據結構。但在存儲空間壓縮方面卻使用普遍。css
bitmap就是用一個bit位來標記某個元素是否存在:1表示存在,0表示不存在;而2-bitmap就是用兩個bit爲來標記某個元素出現的次數:00出現0次,01出現1次。10出現2次及其以上。11無心義。html
2-bitmap在內存中的表演示樣例如如下:
[0] [1] [2] ……
|00 00 00 00|00 00 00 00|00 00 00 00| ……
3 2 1 0 7 6 5 4 11 10 9 8 ……
面試
不論什麼一個可採用數組做爲輔助標記來解決的問題都可以用bitmap來解決,因爲用數組的每個元素做爲標記的話,用bit相同可以做爲標記。當數據量比較小時,有時候爲了操做方便,可直接採用數組。但當數據量很是大的時候,因爲內存大小的緣由或題目限定了可用內存大小,數組就再無法解決咱們的問題。 而此時bitmap就顯示出其空間壓縮的威力了:假設用char型的數組,標記相同範圍內的數bitmap最多可以節省8倍空間,2-bitmap可節省4倍空間。若採用int型的數組。bitmap和2-bitmap可以分別最多節省32倍、16倍的空間。數組
bitmap能用來處理如下問題:數據結構
(1)、字符串方面函數
一、C語言str系列庫函數之strtok()、strspn()、strcspn()和strpbrk()函數都用到了bitmap。post
詳見C語言str系列庫函數之strtok()、C語言str系列庫函數之strspn()、strcspn()和strpbrk();
大數據
二、推斷一個字符串B中的字符是否都在還有一個字符串A中出現(網上能搜到)。google
跟上面第1條博文連接裏的十分類似。spa
三、在一個字符串中找到第一個僅僅出現一次的字符(google面試題、網上能搜到)。
可以用26維的數組統計26個字母出現的次數。而後順序查找統計表直到查到到結果爲1的就是要查到的字符。
也可以採用2-bitmap,更節省空間。
(2)、大數據
一、在2.5億個整數找出不反覆的整數,內存不足以容納着2.5億個整數
二、騰訊面試題:給40億個不反覆的unsigned int的整數。沒排過序的,而後再給一個數,怎樣高速推斷這個數是否在那40億個數其中?
對於問題1。整數多是正數也多是負數,首先僅僅考慮正整數狀況,採用2Bitmap方法,用00表示不存在,01表示出現1次,10表示出現2次及以上,此方法總共需要的內存2^31*2bit = 1Gb = 128MB(32位的正整數有2^31個,每個存儲需要2bit,因此就是1Gb,換成字節就是128MB),這樣內存就應該可以容納了,最後在處理全然部的數後,僅僅要輸出相應位爲01的數就能夠。
假設這2.5億個數裏面既有正數又有負數那麼就用兩個2Bitmap分別存儲正數和負數(取絕對值存儲),零就隨便放。這是所需要的內存是256MB。
對於問題2,直接用Bitmap就能夠,0表示存在,1表示不存在。
不少其它關於用bitmap來解決這個問題的博文:十七道海量數據處理面試題與Bit-map具體解釋、http://blog.csdn.net/v_july_v/article/details/7382693
2-bitmap的使用關鍵在於怎樣操縱位,如下是一個演示樣例代碼:
#include <stdio.h> #include <memory.h> #include <stdlib.h> #define N 1024*1024*1024 unsigned char bitmap[1 + N / 4]; //2-bitmap void set(int x,int num) { int m = x >> 2; //x / 4; int n = x & 0x3; //x % 4; //bitmap[m] &= ~((0x3<<(2*n)) & 0xff); //bitmap[m] |= ((num&0x3)<<(2*n) & 0xff); bitmap[m] &= ~(0x3 << (2*n)); bitmap[m] |= ((num & 0x3) << (2*n)); } void clear(int x) { int m = x >> 2; //x / 4; int n = x & 0x3; //x % 4; //bitmap[m] &= ~((0x3<<(2*n)) & 0xff); //0xff可以去掉 bitmap[m] &= ~(0x3 << (2*n)); } unsigned get(int x) { int m = x >> 2; int n = x & 0x3; return (bitmap[m] & (0x3 << (2*n))) >> (2*n); } void add(int x) { set(x, get(x) + 1); } int main() { int a[8] = {1, 3, 1, 4, 5, 5, 5, 5}; //找出數組a中不反覆的元素 memset(bitmap, 0, sizeof(bitmap)); //清空位圖 for (int i = 0;i < 8; i++) { unsigned val = get(a[i]); //00、0一、10 if (val <= 1) //a[i]在bitmap的位序列爲10時表示出現最少2次 set(a[i], val+1); } //現在可以查看每個元素出現的次數 for (int i = 0;i < 8; i++) printf("%d %d\n", a[i], get(a[i])); putchar('\n'); //假設要推斷某個數x是否存在於數組a中,直接推斷get(x)的值 //get(x) > 0 ? 存在 : 不存在 //如下的代碼輸出在a數組中僅出現1次的數 for (int i = 0; i < 2;i++) //這裏實際上僅僅需要用到16bit for(int j = 0; j < 4; j++) { int x = (i << 2 | (j & 0x3)); //獲得存在這個位置的數是多大 int val = get(x); if (val == 1) printf("%d ", x); } getchar(); return 0; }