bitmap应用及实现(哈希表)

当实现时间复杂度优化的时候,经常会用到hash表来存储一些数据,然后查找,来提高时间复杂度。如果直接设置int arr[]数组,对于大数据来说,会需要很大的空间来存储,严重影响效率。而bitmap是用位来存储数据的:如果只是表示有无,每个数据分配1位;如果表示多个,可能需要给每个数据分配多位(例如统计大数据中不重复的个数,用0、1、2来表示状态)

bitmap在对数据进行排序时,其复杂度为O(n)。当然这是拿空间换来的。与bitmap类似的还有Bloom filter,Bloom filter可以看做是对bit-map的扩展。


样例1:对数组array[4,7,2,5,3]排序

bitmap采用的是以空间换时间的思想,数组中最大元素值为7,所以在内存中开辟8位的存储空间,存储空间大小的确定方法是(元素最大值/8+1),之所以除以8,是因为开辟空间的时候以byte为单位,1byte=8bit。

开辟8位的空间后,每位初始化为0,如下表:

0号位1号位2号位3号位4号位5号位6号位7号位
00000000

开始遍历array数组,array[0]=4时,则将4号位’置1,变为下表:

0号位1号位2号位3号位4号位5号位6号位7号位
00001000

array[1]=7时,则将7号位置1,变为下表:

0号位1号位2号位3号位4号位5号位6号位7号位
00001001

直至遍历完array数组,空间各位如下表:

0号位1号位2号位3号位4号位5号位6号位7号位
00111101

从头开始遍历空间中各位,不为0的输出其为号,得:2,3,4,5,7,其效率为O(n)=8


样例2:array[1,2,5,8,10]为例,涉及到两个字节

初始化空间如下表:

0号位1号位2号位



7号位8号位




15号位15号位
0000000000000000

申请时以字节申请空间:g_bitmap = (char *)malloc((size/8+1)*sizeof(char));
此时将上表从7号位处分开(即2个字节的空间,定义字节数组byte[]):

byte[0]
0号位1号位...7号位
00...0

byte[1]
0号位1号位...7号位
00...0

读取array[0]=1时,

array[0]/8=0——>byte[0]

array[0]%8=1——>1号位,即将byte[0]中的1号位置1,

读取array[4]=10时,

array[0]/8=1——>byte[1]

array[0]%8=2——>2号位,即将byte[1]中的2号位置1,

最后,空间变为:

byte[0]
0号位1号位2号位3号位4号位5号位6号位7号位
01100100

byte[1]
0号位1号位2号位3号位4号位5号位6号位7号位
10100000

遍历每个byte[]下的每位,为1的输出(逆运算得到该位在array[]中的值),


实现代码:

bitmap.h

[cpp]  view plain copy
  1. /* 
  2.  *bitmap的c语言实现 
  3.  *作者:bitileaf 
  4.  *时间:2010-12-18 14:12 
  5.  */  
  6. #ifndef _BITMAP_H_  
  7. #define _BITMAP_H_  
  8.   
  9. /* 
  10.  *功能:初始化bitmap 
  11.  *参数: 
  12.  *size:bitmap的大小,即bit位的个数 
  13.  *start:起始值 
  14.  *返回值:0表示失败,1表示成功 
  15.  */  
  16. int bitmap_init(int size, int start);  
  17.   
  18. /* 
  19.  *功能:将值index的对应位设为1 
  20.  *index:要设的值 
  21.  *返回值:0表示失败,1表示成功 
  22.  */  
  23. int bitmap_set(int index);  
  24.   
  25. /* 
  26.  *功能:取bitmap第i位的值 
  27.  *i:待取位 
  28.  *返回值:-1表示失败,否则返回对应位的值 
  29.  */  
  30. int bitmap_get(int i);  
  31.   
  32. /* 
  33.  *功能:返回index位对应的值 
  34.  */  
  35. int bitmap_data(int index);  
  36.   
  37. /*释放内存*/  
  38. int bitmap_free();  
  39.   
  40. #endif  

bitmap.c

[cpp]  view plain copy
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <string.h>  
  4. #include "bitmap.h"  
  5.   
  6. unsigned char *g_bitmap = NULL;  
  7. int g_size = 0;  
  8. int g_base = 0;  
  9.   
  10. int bitmap_init(int size, int start)  
  11. {  
  12.     g_bitmap = (char *)malloc((size/8+1)*sizeof(char));  
  13.     if(g_bitmap == NULL)  
  14.         return 0;  
  15.     g_base = start;  
  16.     g_size = size/8+1;  
  17.     memset(g_bitmap, 0x0, g_size);  
  18.     return 1;  
  19. }  
  20.   
  21. int bitmap_set(int index)  
  22. {  
  23.     int quo = (index-g_base)/8 ;  
  24.     int remainder = (index-g_base)%8;  
  25.     unsigned char x = (0x1<<remainder);  
  26.     if( quo >= g_size)  
  27.         return 0;  
  28.     g_bitmap[quo] |= x;  
  29.     return 1;   
  30. }  
  31.   
  32. int bitmap_get(int i)  
  33. {  
  34.     int quo = (i)/8 ;  
  35.     int remainder = (i)%8;  
  36.     unsigned char x = (0x1<<remainder);  
  37.     unsigned char res;  
  38.     if( quo >= g_size)  
  39.         return -1;  
  40.     res = g_bitmap[quo] & x;  
  41.     return res > 0 ? 1 : 0;   
  42. }  
  43.   
  44. int bitmap_data(int index)  
  45. {  
  46.     return (index + g_base);  
  47. }  
  48.   
  49. int bitmap_free()  
  50. {  
  51.     free(g_bitmap);  
  52. }  

测试程序:bitmap_test.c

[cpp]  view plain copy
  1. #include <stdio.h>  
  2. #include "bitmap.h"  
  3.   
  4. int main()  
  5. {  
  6.     int a[] = {5,8,7,6,3,1,10,78,56,34,23,12,43,54,65,76,87,98,89,100};  
  7.     int i;  
  8.     bitmap_init(100, 0);  
  9.     for(i=0; i<20; i++)  
  10.         bitmap_set(a[i]);  
  11.     for(i=0; i<100; i++)  
  12.     {  
  13.         if(bitmap_get(i) > 0 )  
  14.             printf("%d ", bitmap_data(i));  
  15.     }  
  16.     printf("\n");  
  17.     bitmap_free();  
  18.     return 0;  
  19. }  

评注:

bitmap_init()函数:

g_size = size/8+1表示需要多少字节;

g_base = start;表示从几开始(例如数据都是在100到200范围内,那么start为100)

调用memset函数初始化为0.


bitmap_set()函数:

quo = (index-g_base)/8 来求存储在哪个字节当中;

remainder = (index-g_base)%8;表示在字节中的哪一位

x = (0x1<<remainder);为remainder位置1

g_bitmap[quo] |= x;设置位


bitmap_get()函数:这个函数传递的参数是0--> size-start,实际值通过bitmap_data()函数转化

res = g_bitmap[quo] & x;求出对应位的值


【问题实例】

1)已知某个文件内包含一些电话号码,每个号码为8位数字,统计不同号码的个数。

8位最多99 999 999,大概需要99m个bit,大概10几m字节的内存即可。 (可以理解为从0-99 999 999的数字,每个数字对应一个Bit位,所以只需要99M个Bit==1.2MBytes,这样,就用了小小的1.2M左右的内存表示了所有的8位数的 电话)

2)海量日志数据,提取出某日访问百度次数最多的那个IP。
    此题,在我之前的一篇文章:“从头到尾彻底解析Hash表”算法里头有所提到,当时给出的方案是:IP的数目还是有限的,最多2^32个,所以可以考虑使用hash将ip直接存入内存,然后进行统计。
    再详细介绍下此方案:首先是这一天,并且是访问百度的日志中的IP取出来,逐个写入到一个大文件中。注意到IP是32位的,最多有个2^32个IP。同样可以采用映射的方法,比如模1000,把整个大文件映射为1000个小文件,再找出每个小文中出现频率最大的IP(可以采用hash_map进行频率统计,然后再找出频率最大的几个)及相应的频率。然后再在这1000个最大的IP中,找出那个频率最大的IP,即为所求。

3)2.5亿个整数中找出不重复的整数的个数,内存空间不足以容纳这2.5亿个整数。

方案1:采用2-Bitmap(每个数分配2bit,00表示不存在,01表示出现一次,10表示多次,11无意义)进行,共需内存内存,还可以接受。然后扫描这2.5亿个整数,查看Bitmap中相对应位,如果是00变01,01变10,10保持不变。所描完事后,查看bitmap,把对应位是01的整数输出即可。
方案2:也可采用与第1题类似的方法,进行划分小文件的方法。然后在小文件中找出不重复的整数,并排序。然后再进行归并,注意去除重复的元素。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值