位图(Bit-Map)使用位数组来存取元素,判断某些元素是否存在。由于使用位为单元来存取元素,因此大大节省了存取空间。适用于海量数据的查找,删除及判重。
先介绍比特编位方法,比特位从右往左位数升高,第一位编号为0,最高位编号为N-1。对于一个int a[4]的数组而言,由于一个int型占用4字节,一字节占8bit,因此一共有128bit,其中
a[0]:0----31
a[1]:32------63
a[2]:64------95
a[3]:96------127
这个数组的128bit可以用来表示128个不同的数。
对于位图中有两个需要说明的运算
利用位运算&实现取模运算%
位运算跟取模运算之间有着微妙的联系,确实很微妙,因为只有在某些特定的情况下才能使用位运算实现
先看个例子,100%32,100&31,经过计算,你会发现结果是一样的,那什么时候才会成立呢?这种计算对前一个数没有要求,不过对后一个的要求比较高,看一看32和31的二进制(统一用八位)表示,32=0010 0000,31=0001 1111,也就是说利用要利用这种算法,后一个数必须满足这样的条件,在取模运算中,该数的二进制表示只能有一位是1,也就是说,这个数必须是2的N次幂;在进行位运算的时候则取该数的前一个数的二进制表示来进行。
字节位置=数据/32(采用位运算将数据右移5位)
位位置=数据%32(采用位运算数据&0x1f)
比如说现在我们有100这个数,于是,我们要将第100位置为1,也就是说,我们最少得有100位,100位等于12.5字节,因为一个int型是4个字节,所以我们必须定义一个这样的数组int[4],数组的位模型可以参考上面。现在,我们要对这个数组的100位进行置位操作嘛,那首先我得先知道100是在这个数组中的第几个元素嘛,所以,使用100/32,我现在要第100位,然后,每个数组元素是32位,进行运算之后,我就得到了我要置的位在那一个元素了,那确定了元素之后,我得知道,我要置位的是元素的哪一个位嘛,所以我就得知道求余的结果咯(纯粹数学知识的),所以就100%32咯,然后根据我上面的分析,模运算%与位运算&之间的一个微妙关系,那么我就可以采用这样的运算方式100&0x1F(0x1F是其实就是32),得到要置位的位置了。
#include <iostream>
using namespace std;
void SetBit(int bit)
{
a|=(1<<bit);
}
void ClearBit(int bit)
{
a&=~(1<<bit);
}
void SetBit(int n,int *bit) //将逻辑位置为n的二进制位置为1
{
bit[n>>5] |= (1<<((n&0x1F)-1)); //n>>SHIFT右移5位相当于除以32求算字节位置,n&0x1F相当于对32取余即求位位置。
}
void ClearBit(int n,int *bit)//将逻辑位置为n的二进制位置0
{
bit[n>>5] &= ~(1<<((n&0x1F)-1)); //将逻辑位置为n的二进制位置0,原理同set操作
}
int TestBit(int n,int *bit)
{
return bit[n>>5] & (1<<((n&0x1F)-1)); //测试逻辑位置为n的二进制位是否为1,如果为1,则返回非零值,注意,不是返回1,是返回非零值;如果为0,则返回0
}
int main(int argc, char* argv[])
{
int a[100]={11,21,31,41,51,61,71,81,91,
2,12,22,32,42,52,62,72,82,92,
3,13,23,33,43,53,63,73,83,93,
4,14,24,34,44,54,64,74,84,94,
5,15,25,35,45,55,65,75,85,95,
6,16,36,56,66,76,86,96,
7,17,27,37,47,57,67,77,87,97,
8,18,28,38,48,58,68,78,88,98,
9,19,29,39,49,59,69,79,89,99,
10,100};
int bit[4];
int i;
for(i=0;i<4;i++)//将所以的比特位均置0
{
bit[i]=0;
}
for(i=0;i<100;i++)//根据数组里面的数据,将相应的比特位置为1
{
SetBit(a[i],bit);
}
for(i=1;i<=100;i++)
{
if(TestBit(i,bit)==0)
{
cout<<i<<" is not exist"<<endl;
}
else
{
cout<<i<<" is exist"<<endl;
}
}
return 0;
}