1. 程式人生 > >bitmap與2-bitmap使用總結

bitmap與2-bitmap使用總結

int 處理 == link stdio.h 題目 可用 內存大小 整數

bitmap是一種簡單的數據結構。但在存儲空間壓縮方面卻使用廣泛。

bitmap就是用一個bit位來標記某個元素是否存在:1表示存在,0表示不存在;而2-bitmap就是用兩個bit為來標記某個元素出現的次數:00出現0次,01出現1次。10出現2次及其以上。11無意義。

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)、字符串方面

1、C語言str系列庫函數之strtok()、strspn()、strcspn()和strpbrk()函數都用到了bitmap。

詳見C語言str系列庫函數之strtok()C語言str系列庫函數之strspn()、strcspn()和strpbrk()

2、推斷一個字符串B中的字符是否都在還有一個字符串A中出現(網上能搜到)。

跟上面第1條博文鏈接裏的十分相似。

3、在一個字符串中找到第一個僅僅出現一次的字符(google面試題、網上能搜到)。

能夠用26維的數組統計26個字母出現的次數。然後順序查找統計表直到查到到結果為1的就是要查到的字符。

也能夠採用2-bitmap,更節省空間。

(2)、大數據

1、在2.5億個整數找出不反復的整數,內存不足以容納著2.5億個整數

2、騰訊面試題:給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、01、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;
}

參考:http://blog.csdn.net/acceptedxukai/article/details/9025493



bitmap與2-bitmap使用總結