bitMap浅析

基本思想:

       所谓的BitMap就是用一个bit位来标记某个元素所对应的value,而key即是该元素,由于BitMap使用了bit位来存储数据,因此可以大大节省存储空间。

举例:

  这此我用一个简单的例子来详细介绍BitMap算法的原理。假设我们要对0-7内的5个元素(4,7,2,5,3)进行排序(这里假设元素没有重复)。我们可以使用BitMap算法达到排序目的。要表示8个数,我们需要8个byte。

  1.首先我们开辟一个字节(8byte)的空间,将这些空间的所有的byte位都设置为0

  2.然后便利这5个元素,第一个元素是4,因为下边从0开始,因此我们把第五个字节的值设置为1

        即:00000100

  3.然后再处理剩下的四个元素,最终8个字节的状态如下图

       即:00111101

  4.现在我们遍历一次bytes区域,把值为1的byte的位置输出(2,3,4,5,7),这样便达到了排序的目的

  从上面的例子我们可以看出,BitMap算法的思想还是比较简单的,关键的问题是如何确定10进制的数到2进制的映射图

MAP映射:

  假设需要排序或则查找的数的总数N=100000000,BitMap中1bit代表一个数字,1个int = 4Bytes = 4*8bit = 32 bit,那么N个数需要N/32 int空间。所以我们需要申请内存空间的大小为int a[1 + N/32],其中:a[0]在内存中占32为可以对应十进制数0-31,依次类推:

  a[0]-----------------------------> 0-31

  a[1]------------------------------> 32-63

  a[2]-------------------------------> 64-95

  a[3]--------------------------------> 96-127

  那么十进制数如何转换为对应的bit位,下面介绍用位移将十进制数转换为对应的bit位:

  1.求十进制数在对应数组a中的下标

  十进制数0-31,对应在数组a[0]中,32-63对应在数组a[1]中,64-95对应在数组a[2]中………,使用数学归纳分析得出结论:对于一个十进制数n,其在数组a中的下标为:a[n/32]

  2.求出十进制数在对应数a[i]中的下标

  例如十进制数1在a[0]的下标为1,十进制数31在a[0]中下标为31,十进制数32在a[1]中下标为0。 在十进制0-31就对应0-31,而32-63则对应也是0-31,即给定一个数n可以通过模32求得在对应数组a[i]中的下标。

  3.位移

  对于一个十进制数n,对应在数组a[n/32][n%32]中,但数组a毕竟不是一个二维数组,我们通过移位操作实现置1

  a[n/32] |= 1 << n % 32
  移位操作:
  a[n>>5] |= 1 << (n & 0x1F)

  n & 0x1F 保留n的后五位 相当于 n % 32 求十进制数在数组a[i]中的下标

代码:

#include<stdio.h>
#include <stdlib.h>
#include <string.h>

#define N  10000000
int *a = NULL;

void addValue(int n){
    //row = n / 32 
    int row = n >> 5;
    //a[row] |= n % 32
    a[row] |= 1 << (n & 0x1F);
}
/*
    eg:n=5  
*/
int exits(int n){
    int row = n >> 5; //row 等于 0
    int tmp = 1 << (n & 0x1F); //(n & 0x1F)=5, tmp=01000000
    int res = a[row] & (tmp);  //当前环境为小端(是指数据的高字节保存在内存的高地址中)
    return (res == tmp);
}

void display(int row){
    char value[4] = {0};
    char map_value[64] = {0};
    int tmp_byte = 0;
    int i = 0, j = 0;
    for(i=0;i<row;i++){
	int temp = a[i];
	for(j=0;j<32;j++){
		tmp_byte = temp & 1; //十进制转为二进制,配合temp >> 1使用
		bzero(value, 4);
		sprintf(value, "%d", tmp_byte);
		strcat(map_value, value);
		(j == 31)? strcat(map_value, "\n"):strcat(map_value, ",");
		temp >>= 1;
			
	}
	printf("a[%d] %s", i, map_value);
	bzero(map_value, 64);
	}
}

int main(int argc, char **argv)
{
    int i = 0;
    a = (int *)calloc(sizeof(int), N/32 + 1);
    int num[] = {1,5,30,32,64,56,159,120,21,17,35,45};
    for(i=0;i<sizeof(num)/sizeof(int);i++){
            addValue(num[i]);
    }
    int temp = atoi(argv[1]);
    if(exits(temp)){
        printf("temp:%d exists\n", temp);
    }
    else
    {
	printf("temp:%d not exists\n", temp);
    }	
	
    display(6);
    free(a);
    a = NULL;
    return 0;
}

实际应用:

1.Bit-map应用之快速排序
  假设我们要对0-7内的5个元素(4,7,2,5,3)排序(这里假设这些元素没有重复),我们就可以采用Bit-map的方法来达到排序的目的。要表示8个数,我们就只需要8个Bit(1Bytes),首先我们开辟1Byte的空间,将这些空间的所有Bit位都置为0,
  缺点:
    所有的数据不能重复。即不可对重复的数据进行排序和查找。

2. Bit-map应用之快速去重
  2.5亿个整数中找出不重复的整数的个数,内存空间不足以容纳这2.5亿个整数。 
  首先,根据“内存空间不足以容纳这2.5亿个整数”我们可以快速的联想到Bit-map。下边关键的问题就是怎么设计我们的Bit-map来表示这2.5亿个数字的状态了。其实这个问题很简单,一个数字的状态只有三种,分别为不存在,只有一个,有重复。因此,我们只需要2bits就可以对一个数字的状态进行存储了,假设我们设定一个数字不存在为00,存在一次01,存在两次及其以上为11。那我们大概需要存储空间几十兆左右。
  接下来的任务就是遍历一次这2.5亿个数字,如果对应的状态位为00,则将其变为01;如果对应的状态位为01,则将其变为11;如果为11,,对应的转态位保持不变。
  最后,我们将状态位为01的进行统计,就得到了不重复的数字个数,时间复杂度为O(n)。

3. Bit-map应用之快速查询
  同样,我们利用Bit-map也可以进行快速查询,这种情况下对于一个数字只需要一个bit位就可以了,0表示不存在,1表示存在。假设上述的题目改为,如何快速判断一个数字是够存在于上述的2.5亿个数字集合中。
  同之前一样,首先我们先对所有的数字进行一次遍历,然后将相应的转态位改为1。遍历完以后就是查询,由于我们的Bit-map采取的是连续存储(整型数组形式,一个数组元素对应32bits),我们实际上是采用了一种分桶的思想。一个数组元素可以存储32个状态位,那将待查询的数字除以32,定位到对应的数组元素(桶),然后再求余(%32),就可以定位到相应的状态位。如果为1,则代表改数字存在;否则,该数字不存在。

  • 0
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值