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号位
0 0 0 0 0 0 0 0

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

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

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

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

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

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

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


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

初始化空间如下表:

0号位 1号位 2号位



7号位 8号位




15号位 15号位
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

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

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

byte[1]
0号位 1号位 ... 7号位
0 0 ... 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号位
0 1 1 0 0 1 0 0

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

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


实现代码:

bitmap.h

/*
 *bitmap的c语言实现
 *作者:bitileaf
 *时间:2010-12-18 14:12
 */
#ifndef _BITMAP_H_
#define _BITMAP_H_

/*
 *功能:初始化bitmap
 *参数:
 *size:bitmap的大小,即bit位的个数
 *start:起始值
 *返回值:0表示失败,1表示成功
 */
int bitmap_init(int size, int start);

/*
 *功能:将值index的对应位设为1
 *index:要设的值
 *返回值:0表示失败,1表示成功
 */
int bitmap_set(int index);

/*
 *功能:取bitmap第i位的值
 *i:待取位
 *返回值:-1表示失败,否则返回对应位的值
 */
int bitmap_get(int i);

/*
 *功能:返回index位对应的值
 */
int bitmap_data(int index);

/*释放内存*/
int bitmap_free();

#endif

bitmap.c

#include 
#include 
#include 
#include "bitmap.h"

unsigned char *g_bitmap = NULL;
int g_size = 0;
int g_base = 0;

int bitmap_init(int size, int start)
{
    g_bitmap = (char *)malloc((size/8+1)*sizeof(char));
    if(g_bitmap == NULL)
        return 0;
    g_base = start;
    g_size = size/8+1;
    memset(g_bitmap, 0x0, g_size);
    return 1;
}

int bitmap_set(int index)
{
    int quo = (index-g_base)/8 ;
    int remainder = (index-g_base)%8;
    unsigned char x = (0x1<= g_size)
        return 0;
    g_bitmap[quo] |= x;
    return 1; 
}

int bitmap_get(int i)
{
    int quo = (i)/8 ;
    int remainder = (i)%8;
    unsigned char x = (0x1<= g_size)
        return -1;
    res = g_bitmap[quo] & x;
    return res > 0 ? 1 : 0; 
}

int bitmap_data(int index)
{
    return (index + g_base);
}

int bitmap_free()
{
    free(g_bitmap);
}

测试程序:bitmap_test.c

#include 
#include "bitmap.h"

int main()
{
    int a[] = {5,8,7,6,3,1,10,78,56,34,23,12,43,54,65,76,87,98,89,100};
    int i;
    bitmap_init(100, 0);
    for(i=0; i<20; i++)
        bitmap_set(a[i]);
    for(i=0; i<100; i++)
    {
        if(bitmap_get(i) > 0 )
            printf("%d ", bitmap_data(i));
    }
    printf("\n");
    bitmap_free();
    return 0;
}

评注:

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<

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题类似的方法,进行划分小文件的方法。然后在小文件中找出不重复的整数,并排序。然后再进行归并,注意去除重复的元素。




你可能感兴趣的:(数据结构)