bitmap:
说明:采用一个bit来标记某个value,可以大大节省存储空间。
优点是占用内存少,比如n为一亿(10000 0000),只需要n/8=1250 0000个byte,约等于12mb。缺点为不能重复数据进行排序和查找
思想:利用一个byte中的8个bit来表示8个数。某数出现,利用映射将对应bit位置1。
比如元素3,在8bit的映射为
再来个元素5,在8bit的映射为
映射表为:
a[0]->0~7;
a[1]->8~15;
a[2]->16~23;
…….
具体实现:(例子为32位)
#include
#define bitsperword 32
#define shift 5
#define mask 0x1f
//#define n 10000000
#define n 100
int a[1 n/bitsperword];//申请内存的大小
void set(int i) {
// a[i>>shift] |= (1<<(i& mask));
a[i/bitsperword] |= (0x01<<(i%bitsperword));
}
//clr 初始化所有的bit位为0
void clr(int i) {
// a[i>>shift] &= ~(1<<(i & mask));
a[i/bitsperword] &= ~(0x01<<(i%bitsperword));
}
//test 测试所在的bit为是否为1
int test(int i){
return a[i>>shift] & (0x01<<(i & mask));
}
int main()
{ int i;
for (i = 0; i < n; i )
clr(i);
for(i=0;i
a[i/bitsperword]或a[i>>shift],实现i/32,即求出操作bit所在数的下标。
0x01<<(i%bitsperword)或(1<<(i& mask)),取得该操作bit的具体位置。
实例:
1、 已知某个文件内包含一些电话号码,每个号码为8位数字,统计不同号码的个数。
思路:8位数,0000 0000~9999 9999,范围为一亿。需要10^8个bit,12m左右字节。
实现:
int const narraysize = 100000000 /(sizeof(int)*8) 1;
int bitmap[narraysize];
void mapphonenumbertobit(int nnumber){
bitmap[nnumber/ (8*sizeof(int))] |= 0x00000001<<(nnumber%(8*sizeof(int)));
}
2、2.5亿个整数中找出不重复的整数的个数,内存空间不足以容纳这2.5亿个整数。
思路:用两个bit来表示一个数。0表示整数未出现,1表示出现一次,2表示出现多次。在遍历这些数的时候,如果对应位置的值是0,则将其置为1;如果是1,将其置为2;如果是2,则保持不变。注意控制与置位的分离。
实现
#include
unsigned char flags[1000]; //数组大小自定义
unsigned get_val(int idx) {
// | 8 bit |
// |00 00 00 00| //映射3 2 1 0
// |00 00 00 00| //表示7 6 5 4
// ……
// |00 00 00 00|
int i = idx/4; //一个char 表示4个数,
int j = idx%4;
unsigned ret = (flags[i]&(0x3<<(2*j)))>>(2*j);
//0x3是0011 j的范围为0-3,因此0x3<<(2*j)范围为00000011到11000000 如idx=7 i=1 ,j=3 那么flags[1]&11000000, 得到的是|00 00 00 00|
//表示7 6 5 4
return ret;
}
unsigned set_val(int idx, unsigned int val) {
int i = idx/4;
int j = idx%4;
unsigned tmp = (flags[i]&~((0x3<<(2*j))&0xff)) | (((val%4)<<(2*j))&0xff);
flags[i] = tmp;
return 0;
}
unsigned add_one(int idx)
{
if (get_val(idx)>=2) { //这一位置上已经出现过了??
return 1;
} else {
set_val(idx, get_val(idx) 1);
return 0;
}
}
//只测试非负数的情况;
//假如考虑负数的话,需增加一个2-bitmap数组.
int a[]={1, 3, 5, 7, 9, 1, 3, 5, 7, 1, 3, 5,1, 3, 1,10,2,4,6,8,0};
int main() {
int i;
memset(flags, 0, sizeof(flags));
printf("原数组为:");
for(i=0;i < sizeof(a)/sizeof(int); i) {
printf("%d ", a[i]);
add_one(a[i]);
}
printf("\r\n");
printf("只出现过一次的数:");
for(i=0;i < 100; i) {
if(get_val(i) == 1)
printf("%d ", i);
}
printf("\r\n");
return 0;
}
3、给你一个文件,里面包含40亿个整数,写一个算法找出该文件中不包含的一个整数, 假设你有1gb内存可用。如果你只有10mb的内存呢?
思路:40亿个整数(40*10^8*4byte~16gb)。
a、 内存为1g
显然无法将16g的数据直接放入1g内存,采用bitmap算法,40*10^8bit= 0.5g(大约),满足1g的内存。
b、 内存为10m
如果我们只有10mb的内存,明显使用bitmap也无济于事了。 内存这么小,注定只能分块处理。比如分块的大小是1000,那么第1块的范围就是0到999,第2块的范围就是1000 到1999……
实际上并不保存这些数,而是给每一个块设置一个计数器。 这样每读入一个数,就在它所在的块对应的计数器加1。处理结束之后,我们找到一个块,它的计数器值小于块大小(1000),说明了这一段里面一定有数字是文件中所不包含的。然后我们单独处理这个块即可。