bitmap是一个非常有用的结构。所谓的bitmap是用一个bit位来标记某个元素对应的value,而key即是该元素。
例如bitmap[key]=0x1;表示key这个元素有出现。
由于采用了bit为单位来存储数据,因此在存储空间方面,可以大大节省。
使用范围:可进行数据的快速查找,判重,删除,一般来说数据范围在int的10倍以下.
问题实例:
1.已知某个文件包含一些电话号码,每个号码为8位数字,统计不同号码的个数。
思路:
8位最多为9999 9999,大概需要99Mb,即十几MB的内存。
#include <iostream>
using namespace std;
#include <fstream>
const int MAX = 99999999;
char *g_bitmap = NULL;
int g_size = 0;
int g_base = 0;
//功能:初始化bitmap
//参数: size:bitmap的大小,即bit位的个数
// start:起始值
//返回值:0表示失败,1表示成功
int bitmap_init(int size, int start)
{
g_size = size / 8 + 1;
g_base = start;
g_bitmap = new char[g_size];
if (g_bitmap == NULL)
{
return 0;
}
memset(g_bitmap, 0x0, g_size);
return 1;
}
//功能:将值index的对应位设为1
//index:要设的值
//返回值:0表示失败,1表示成功
int bitmap_set(int index)
{
int quo = (index - g_base) / 8; //确定所在的字节
int remainder = (index - g_base) % 8; //字节内的偏移
unsigned char x = (0x1 << remainder);
if (quo > g_size)
return 0;
g_bitmap[quo] |= x; //所在字节内的特定位置为1
return 1;
}
//功能:取bitmap第i位的值
//i:待取位
//返回值:-1表示失败,否则返回对应位的值
int bitmap_get(int i)
{
int quo = (i) / 8;
int remainder = (i) % 8;
unsigned char x = (0x1 << remainder);
unsigned char res;
if (quo > g_size)
return -1;
res = g_bitmap[quo] & x;
return res > 0 ? 1 : 0;
}
//功能:返回index位对应的值
int bitmap_data(int index)
{
return (index + g_base);
}
//释放内存
int bitmap_free()
{
delete[] g_bitmap;
return 0;
}
int main()
{
bitmap_init(MAX, 0);
ifstream in;
in.open("phoneNumber.txt");
if (!in.good()) {
cout << "open failed" << endl; return 1;
}
int record;
int count = 0;
while (!in.eof())
{
in >> record;
bitmap_set(record);
}
for (int i = 0; i <= MAX; i++)
{
if (bitmap_get(i) > 0)
{
cout << bitmap_data(i) << " ";
count++;
}
}
cout << endl;
cout << count << endl;
bitmap_free();
in.close();
return 0;
}
2. 2.5亿个整数中找出不重复的整数的个数,内存空间不足以容纳这2.5亿个整数。
思路:
将bitmap扩展一下,用2bit表示一个数,00表示没有出现,01表示出现一次,10表示出现两次或以上。这就是2bitmap
这样总共需要的内存为:
2^31*2bit=1Gb=128M(其中2^31表示unsigned int的个数)
注意:如果这2.5亿个数里面既有正数,又有负数,就要用两个2bitmap来分别存储,其中负数取绝对值后存储。
#include <iostream>
using namespace std;
#include <fstream>
const int MAX = 200000000;
char *g_bitmap = NULL;
int g_size = 0;
//功能:初始化bitmap
//参数: size:bitmap的大小,即bit位的个数
// start:起始值
//返回值:0表示失败,1表示成功
int bitmap_init(int size)
{
g_size = size / 8 + 1;
g_bitmap = new char[g_size];
if (g_bitmap == NULL)
{
return 0;
}
memset(g_bitmap, 0x0, g_size);
return 1;
}
//功能:将值index的对应位设为1
//index:要设的值
//num:bitmap中已经拥有index的个数
//返回值:0表示失败,1表示成功
int bitmap_set(int index,int num)
{
int quo = (index) / 4; //确定所在的字节
int remainder = (index) % 4; //字节内的偏移
if (quo > g_size)
return 0;
g_bitmap[quo] &= ~((0x3 << (2 * remainder))&0xff); //将index对应位上的数值先清零,但是保证其他位不变
g_bitmap[quo] |= ((num % 4) << (2 * remainder)&0xff); //重新对index的个数赋值
return 1;
}
//功能:取bitmap第i位的值
//i:待取位
//返回值:-1表示失败,否则返回对应位的值
int bitmap_get(int i)
{
int quo = (i) / 4;
int remainder = (i) % 4;
if (quo > g_size)
return -1;
int res = (g_bitmap[quo] & (0x3 << (2 * remainder))) >> (2 * remainder);
return res;
}
//功能:增加一个
unsigned add_one(int index)
{
if (bitmap_get(index) >= 2) return 1; //这一位置已经出现
else bitmap_set(index, bitmap_get(index) + 1);
return 0;
}
//释放内存
int bitmap_free()
{
delete[] g_bitmap;
return 0;
}
int main()
{
bitmap_init(MAX);
ifstream in;
in.open("phoneNumber.txt");
if (!in.good()) {
cout << "open failed" << endl; return 1;
}
int record;
int count = 0;
while (!in.eof())
{
in >> record;
add_one(record);
}
for (int i = 0; i < MAX/2; i++)
{
if (bitmap_get(i) == 1)
{
cout << i << " ";
count++;
}
}
cout << endl;
cout << count << endl;
bitmap_free();
in.close();
return 0;
}