前言
因为之前上数据挖掘的课的老师经常在课上提到这种方法,正好我又不会,于是抽点时间出来写一篇Blog学习一下这种数据结构。
问题导入
给定40亿个不重复的unsigned int的整数,没有排过序的,然后再给一个数,如何快速判断这个数是否在那40亿个数当中。
快速思考
C语言总一个int变量占32位,即4个字节,那么如果用一个40亿的数组来存储的话,需要的空间就是40亿x4Bytes = 160亿 Bytes = 14G,显然计算机的内存这样就爆了。所以就引出了位图的方法。
定义
实际上一个int有32位,而我仅仅拿来存储了32个数,如果我每一位都拿来存储一个数呢?比如一个int型变量,我要存储数15,那么就将第15位上置为1,我要存储20,就把第20位上置为1,以此类推,这样我一个int型变量就能存储32个数。
所申请的int数组如下:
如何确定某一个数A在哪个位置呢?
首先要得到其在那个字节,即A / 32.
其次要得到在哪一位,即 A % 32
采用位运算可以更快的进行计算。
A/32 = A >> 5(向右移动5次)
A%32 = A & (0x1F)
解决
因此上述的问题就可以只用40亿个bit = 1.25亿个Bytes来解决,显然对内存的要求要低许多。思路就是读取40亿个整数,对于每一个数将某一bit置1,然后对于陌生的数,直接判断其对应的位是1还是0即可。
代码
#define MAX 4000000000
#define MASK 0x1F
#define DIGITS 32
int a[MAX/DIGITS];
void set(int num) {//将某数的逻辑位置置1
a[num>>5] |= (1 << (n&MASK)); //num>>5相当于求字节位置,num&MASK相当于求位位置
}
void clear(int num) {//将某数的逻辑位置清0
a[num>>5] &= (~(1<<(n&MASK)));
}
void test(int num) {
return a[num>>5] & (1<<(n&MASK));
}
C++ STL bitmap头文件中提供了bitmap的实现
http://www.cplusplus.com/reference/bitset/bitset/
#include <iostream>
#include<bitset>
using namespace std;
int main(int argc, char *argv[])
{
const int max = 10000000;
int n,i;
bitset<max+1> bit; //初始默认所有二进制位为0
while(scanf("%d",&n)!=EOF)
{
bit.set(n,1); //将第n位置1
}
for(i=0;i<=max+1;i++)
{
if(bit[i]==1)
printf("%d ",i);
}
return 0;
}
其余应用
1.排序
先将数组中所有的数读进去,然后从低位到高位遍历一遍,即可得到升序的结果。
2.两个数组求交集
用两个位图,然后对两个位图进行与操作,即可得到这两个数组的交集。
等等!