瞭解過哈希表,我們接下來看一看哈希表的變形—–位圖。
位圖:顧名思義就是以bit位爲單位,但是需要注意的是我們的位圖並不能存儲我們的數據,而是借用這樣一個結構標記某一個數據是否存在。
如圖所示,如果該數據存在,則就將該位圖中的相應位從0置爲1。
下面我們來實現一下位圖的基本操作:
//以下爲bit_map.h文件內容
#pragma once
#include<stdint.h>
#define BitmapDataType uint64_t
typedef struct Bitmap
{
BitmapDataType *data;
BitmapDataType capacity;//位圖最多能夠容納的個數
}Bitmap;
//初始化
void BitmapInit(Bitmap *bm,uint64_t capacity);
//銷燬
void BitmapDestroy(Bitmap *bm);
//判斷某一位是否爲1
int BitmapTest(Bitmap *bm,uint64_t index);
//設置某一位爲1
void BitmapSet(Bitmap *bm,uint64_t index);
//設置某一位爲0
void BitmapUnset(Bitmap *bm,uint64_t index);
//將位圖全部位置爲1
void BitmapFill(Bitmap *bm);
//將位圖全部位置爲0
void BitmapClear(Bitmap *bm);
//初始化
void BitmapInit(Bitmap *bm,uint64_t capacity)
{
if(bm == NULL)
{
//非法輸入
return;
}
//capacity:指定位圖最多能夠容納多少位
//此處我們使用的uint64_t佔8個字節,64位
//比如:capacity=100,應該容納2個元素纔能有足夠的位表示100位
//比如:capacity=200,應該容納4個元素纔能有足夠的位表示200位
//比如:capacity=300,應該容納5個元素纔能有足夠的位表示300位
//比如:capacity=N,N/(sizeof(uint64_t)*8)+1
bm->capacity = capacity;
//size表示我們申請內存時對應的數組元素個數
uint64_t size = capacity/(sizeof(uint64_t)*8)+1;
bm->data = \
(BitmapDataType*)malloc(sizeof(BitmapDataType)*size);
memset(bm->data,0,sizeof(BitmapDataType)*size);
return;
}
//測試一下
void TestInit()
{
Test_Header;
Bitmap bm;
BitmapInit(&bm,100);
printf("expected bm->capacity = 100,\
actual bm->capacity = %lu\n",bm.capacity);
}
//銷燬
void BitmapDestroy(Bitmap *bm)
{
if(bm == NULL)
{
//非法輸入
return;
}
bm->capacity = 0;
free(bm->data);
return;
}
測試結果:
//此處判斷某一位是否爲1的函數可用於後面的函數的測試
//輔助函數
void GetOffset(uint64_t index,uint64_t *n,uint64_t *offset)
{
//n是數組中的哪一個元素
*n = index/(sizeof(BitmapDataType)*8);
//offset是該元素中的bit位中的哪一位
*offset = index%(sizeof(BitmapDataType)*8);
return;
}
//檢測某一位是否爲1,返回1表示該位爲1,返回0表示該位爲0
int BitmapTest(Bitmap *bm,uint64_t index)
{
if(bm == NULL || index >= bm->capacity)
{
//非法輸入
return 0;
}
uint64_t n,offset;
//找到需要判斷的位置對應於我們數組中的哪一個元素的哪一位
GetOffset(index,&n,&offset);
//先將1左移offset位,結束以後相應位即爲1
//再將其與對應的數組中的元素按位與
//0和1與爲0,1和1與爲1
//按位與運算完畢就可以知道相應位是否爲1
uint64_t ret = bm->data[n] & (0x1ul << offset);
return ret > 0 ? 1:0;
}
//將某一位設置爲1
void BitmapSet(Bitmap *bm,uint64_t index)
{
if(bm == NULL || index >= bm->capacity)
{
//非法輸入
return;
}
uint64_t n,offset;
//找到需要判斷的位置對應於我們數組中的哪一個元素的哪一位
GetOffset(index,&n,&offset);
//將1左移offset位,移動結束以後,需要設置的位置即爲1,其餘位置爲0
//再將其與數組中對應的元素按位或
//0和1或是1,1和1或是1
//有這樣的規則,按位或就不會影響其他位的狀態
bm->data[n] |= (0x1ul << offset);
}
//將某一位設置爲0
void BitmapUnset(Bitmap *bm,uint64_t index)
{
if(bm == NULL || index >= bm->capacity)
{
//非法輸入
return;
}
uint64_t n,offset;
//找到需要判斷的位置對應於我們數組中的哪一個元素的哪一位
GetOffset(index,&n,&offset);
//先將1左移offset位,那麼相應位變爲1其餘位爲0
//再取反,則相應位爲0,其餘位爲1
//在將該結果與對應的數組元素進行按位與
//1和0與結果爲0,0和0與結果爲0(達到將某一位置爲0的效果)
//有這樣的規則,則我們的按位與操作不會影響其他的位的狀態
bm->data[n] &= ~(0x1ul << offset);
}
//測試一下
void TestSetAndUnset()
{
Test_Header;
Bitmap bm;
BitmapInit(&bm,100);
printf("[將位圖某一位置爲1函數測試結果]\n");
//將第二位設置爲1
BitmapSet(&bm,2);
//非法測試,將第100爲設置爲1
BitmapSet(&bm,100);
int ret1 = BitmapTest(&bm,2);
int ret2 = BitmapTest(&bm,100);
printf("expected ret1 = 1,actual ret1 = %d\n",ret1);
printf("expected ret2 = 0,actual ret2 = %d\n",ret2);
printf("[將位圖某一位置爲0函數測試結果]\n");
//將第二位設置爲0
BitmapUnset(&bm,2);
//非法測試,將第100位設置爲0
BitmapUnset(&bm,100);
int ret3 = BitmapTest(&bm,2);
int ret4 = BitmapTest(&bm,100);
printf("expected ret3 = 0,actual ret3 = %d\n",ret3);
printf("expected ret4 = 0,actual ret4 = %d\n",ret4);
}
測試結果:
//將位圖全部置爲1
void BitmapFill(Bitmap *bm)
{
if(bm == NULL)
{
//非法輸入
return;
}
uint64_t size = bm->capacity/(sizeof(BitmapDataType)*8)+1;
memset(bm->data,0xff,sizeof(BitmapDataType)*size);
}
//將位圖全部置爲0
void BitmapClear(Bitmap *bm)
{
if(bm == NULL)
{
//非法輸入
return;
}
uint64_t size = bm->capacity/(sizeof(BitmapDataType)*8)+1;
memset(bm->data,0x0,sizeof(BitmapDataType)*size);
}
//測試一下
void TestClearAndFill()
{
Test_Header;
Bitmap bm;
BitmapInit(&bm,100);
printf("[將位圖全部置爲1函數測試結果]\n");
//將位圖全部置爲1函數測試
BitmapFill(&bm);
int ret1 = BitmapTest(&bm,50);
printf("expected ret1 = 1,actual ret1 = %d\n",ret1);
ret1 = BitmapTest(&bm,0);
printf("expected ret1 = 1,actual ret1 = %d\n",ret1);
ret1 = BitmapTest(&bm,99);
printf("expected ret1 = 1,actual ret1 = %d\n",ret1);
printf("[將位圖全部置爲0函數測試結果]\n");
//將位圖全部清0函數測試
BitmapClear(&bm);
int ret2 = BitmapTest(&bm,50);
printf("expected ret2 = 0,actual ret2 = %d\n",ret2);
ret2 = BitmapTest(&bm,0);
printf("expected ret2 = 0,actual ret2 = %d\n",ret2);
ret2 = BitmapTest(&bm,99);
printf("expected ret2 = 0,actual ret2 = %d\n",ret2);
}
測試結果: