Bitmap就是用一个bit位来标记某个元素是否存在,而2Bitmap就是用两个bit为来标记某个元素的个数,00,01,10,11(分别表示0,1,2,3,0表示不存在,1表示存在1次,后面依次)。
Bitmap能用来处理下面的两个问题:
(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分别存储正数和负数(取绝对值存储),零就随便放,这是所需要的内存是512MB。
对于问题2,直接用Bitmap即可,0表示存在,1表示不存在。
Bitmap实现:
参考:http://blog.csdn.net/QIBAOYUAN/article/details/5914662
#include <cstdio> #include <iostream> using namespace std; #define WORD 32 #define SHIFT 5 ////移动5个位,左移则相当于乘以32,右移相当于除以32取整 #define MASK 0x1F //16进制下的31 #define N 1024*1024*1024 int bitmap[1 + N / WORD]; /* * 置位函数——用"|"操作符,i&MASK相当于mod操作 * m mod n 运算,当n = 2的X次幂的时候,m mod n = m&(n-1) */ void set(int i) { bitmap[i >> SHIFT] |= (1 << (i & MASK)); } /* 清除位操作,用&~操作符 */ void clear(int i) { bitmap[i >> SHIFT] &= ~(1 << (i & MASK)); } /* 测试位操作用&操作符 */ int test(int i) { return bitmap[i >> SHIFT] & (1 << (i & MASK)); }这里由于int型为32个bit,那么每个bitmap[i]就能保存32个相邻整数是否存在的信息,而为了区分就用余数来表示其位置,例如对于号码 89256,由于89256 mod 32=2789…8,这样我们应该置a[2789]中32位字符串的第8位(从低位数起)为1。
2Bitmap实现:
#include <cstdio> #include <iostream> using namespace std; unsigned char bitmap[1005]; //x表示一个整数,num表示bitmap中已经拥有x的个数 //由于我们只能用2个bit来存储x的个数,所以num的个数最多为3 void set(int x,int num){ int m = x >> 2; int n = x & 3; //将x对于为值上的个数值先清零,但是有要保证其他位置上的数不变 bitmap[m] &= ~((0x3<<(2*n)) & 0xFF); //重新对x的个数赋值 bitmap[m] |= ((num&3)<<(2*n) & 0xFF); } void clear(int x){ int m = x >> 2; int n = x & 3; bitmap[m] &= ~((0x3<<(2*n)) & 0xFF); } unsigned char get(int x){ int m = x >> 2; int n = x & 3; return (bitmap[m] & (0x3<<(2*n))) >> (2*n); } void add(int x){ int num = get(x); set(x,num+1); }上面的&0xFF其实没用,作用就是为了“确保”,unsigned char为8个bit位。
总结:Bitmap是简单快速的数据结构,长用于空间的优化。