哈希扩展——位图

        在之前的介绍过的哈希表中,如果要在表中存放一个整数,此时就要申请一个整型的内存来存放它,一个整型数据在32位或64位平台下都占4个字节。如果现在需要存储的数据非常多,比如说40亿个不重复的数据,就需要160亿个字节来存储,1GB的内存表示10亿个字节,此时就需要16GB的内存来存放这些数据,而我们普通的电脑内存一般都是4G的内存,这显然是不够大的。我们知道,内存中的最小单位是比特位。如果能用一个比特位来存放一个整型,只需要0.5GB的内存。

        一个比特位可以被设置为0或1,如果要表示40亿个数据,可以申请0.5GB的内存。如果要存放的数据为10,就将第10个比特位设置为1。如果要查找的数据为100,就查看第100个比特位处的状态,如果为1说明,100存在于这堆数据中,如果为0说明不存在。像这样用一个比特位来表示一个数是否存在,这种结构就是位图

        位图最大的优点就是节省空间;但是同样它的最大缺点就是只能表示数据是否存在,而不能用来存放数据。

        下面我们就来实现一下位图的基本操作:

1. 结构定义

        其中capacity表示能容纳的元素有多少(单位为比特位),这个要在初始化的时候定长,然后申请空间。若之后觉得空间太小,可以再扩容。

#pragma once

#include <stdio.h>
#include <stdint.h>//uint64_t
#include <stdlib.h>
#include <string.h>                                                                                                                                                     

#define SHOW_NAME printf("\n========================%s=======================\n", __FUNCTION__);

typedef uint64_t BitmapType;//uint64_t类型可跨平台使用,占8字节

typedef struct Bitmap
{
    BitmapType* data;
    uint64_t capacity;
}Bitmap;

2. 初始化

//1.初始化
void BitmapInit(Bitmap* bm, uint64_t capacity)//初始化
{
    if(bm == NULL)//非法输入
        return;
    bm->capacity = capacity;//表示位图能保存的最大数(bit数)
    uint64_t size = bm->capacity/(sizeof(bm->data[0])*8) + 1;//要申请的元素个数
    bm->data = (BitmapType*)malloc(sizeof(BitmapType)*size);
    memset(bm->data, 0, sizeof(BitmapType)*size);//初始化数据
    return;
}

3. 销毁

//2.销毁
void BitmapDestroy(Bitmap* bm)
{
    if(bm == NULL)
        return;
    bm->capacity = 0;
    free(bm->data);
    return;
}

4. 测试函数——测试某个比特位是0还是1

//3.测试函数—输出某一位是0还是 
void GetOffset(uint64_t index, uint64_t* n, uint64_t* offset)//确定要测试的是在第几个元素的第几个比特位
{//n输出元素下标,offset输出比特位置
    *n = index / (sizeof(BitmapType)*8);
    *offset = index % (sizeof(BitmapType)*8);
    return;
}

int BitmapTest(Bitmap* bm, uint64_t index)//该bit位上是1返回1,是0返回0
{//index表示是数组中的第多少位bit
    if(bm == NULL || index >= bm->capacity)//非法输入
        return 0;
    uint64_t n,offset;
    GetOffset(index, &n, &offset);
    
    uint64_t ret = bm->data[n] & (0x1ul << offset);//这里使用0x1ul是为了防止因为类型问题导致移位得到的数据出错
    return ret > 0 ? 1 : 0;
}

5. 设置某个比特位为1

//4.设置某个比特位为1
void BitmapSet(Bitmap* bm, uint64_t index)
{
    if(bm == NULL || index >= bm->capacity)//非法输入
        return;
    uint64_t n,offset;
    GetOffset(index, &n, &offset);
    bm->data[n] |= (0x1ul << offset);//或1 一定为1
    return;
}

6. 设置某个比特位为0

//5.设置某个比特位为0
void BitmapUnSet(Bitmap* bm, uint64_t index)
{
    if(bm == NULL || index >= bm->capacity)//非法输入
        return;
    uint64_t n,offset;
    GetOffset(index, &n, &offset);
    bm->data[n] &= ~(0x1ul << offset);//与0 一定为0
    return;
}

7. 设置所有比特位为1

//6.设置所有比特位为1
void BitmapFill(Bitmap* bm)
{
    if(bm == NULL)//非法输入
        return;
    uint64_t size = bm->capacity/(sizeof(bm->data[0])*8) + 1;
    memset(bm->data, 0xff, sizeof(BitmapType)*size);
    return;
}

8. 设置所有比特位为0

//7.设置所有比特位为0
void BitmapEmpty(Bitmap* bm)
{
    if(bm == NULL)//非法输入
        return;
    uint64_t size = bm->capacity/(sizeof(bm->data[0])*8) + 1;
    memset(bm->data, 0x0, sizeof(BitmapType)*size);
    return;
}

以下是测试函数

void Test()
{
    SHOW_NAME;
    Bitmap bm;
    BitmapInit(&bm, 100);
    printf("capacity: expected is 100, actual is %d\n", bm.capacity);
    printf("expected is 0, actual is %lu\n", bm.data[0]);//uint64_t类型用%lu输出
    BitmapSet(&bm, 4);
    printf("expected is 1, actual is %d\n", BitmapTest(&bm, 4));
    BitmapUnSet(&bm, 7);
    printf("expected is 0, actual is %d\n", BitmapTest(&bm, 7));
    BitmapFill(&bm);
    printf("expected is 1, actual is %d\n", BitmapTest(&bm, 31));
    BitmapEmpty(&bm);
    printf("expected is 0, actual is %d\n", BitmapTest(&bm, 27));
    BitmapDestroy(&bm);
    printf("expected is 0, actual is %d\n", bm.capacity);
}

猜你喜欢

转载自blog.csdn.net/lycorisradiata__/article/details/80467579