本文简介
本文介绍一下 数据结构 BitMap 和用多种语言BitMap的简单实现 。
bitmap 简介
数据结构 Bitmap 的核心思想就是 用 连续的bit
来标志一个 可连续 数据
的有无 。因为他只需要存储数据的有无(布尔值)而不是数据,所以可以大大的降低存储空间。也可以大大加快查询的速度。
bitmap的优缺点
优点
- 节省存储所需要的空间
- 查询速度快,更新速度快。
缺点
- 需要对所有的可能都进行存储
- 数据不可重复
- 数据越紧凑节省内存方面的优点越明显,如果数据过于松散内存甚至不如直接存储。
bitmap 的应用方向
利用bitmap对紧凑数据的占用空间小,操作速度快的特点,我们可以用在:
- 大量数的快速查找与存储
利用数据的不可重复性,我们可以应用在:
- 数据去重
利用数据的有序性,与操作速度快的特点,我们可以应用在
- 对无序不重复数据进行排序
用程序实现 bitmap 的逻辑思想
如果我们需要存储
0
−
7
0-7
0−7 八个数字的有无,那么我们就需要一个 char 类型的数据。其中 每个位就代表这个数值的有无:
如果都没有就是 十六进制的 0x00 二进制如下:
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
如果现在我们需要需要表示 1 , 3 , 5 , 7 1 , 3 , 5 , 7 1,3,5,7 四个数存在那么这个char的值就是:
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|
1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 |
如果我们需要存储的数据的数字不止
0
−
7
0-7
0−7 ,而是
0
−
15
0-15
0−15呢?那么我们就需要一个char 的列表 char[] ,如果什么都没有如下:
{ 0x00 , 0x00}
[1]7(15) | [1]6(14) | [1]5(13) | [1]4(12) | [1]3(11) | [1]2(10) | [1]1(9) | [1]0(8) | [0]7 | [0]6 | [0]5 | [0]4 | [0]3 | [0]2 | [0]1 | [0]0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
如果我们需要表示
2
2
2 这个数字存在则需要在 char[0] 的第二个位置写 1 .是不是很像二维数组。
如下:
{ 0x08 , 0x00 }
[1]7(15) | [1]6(14) | [1]5(13) | [1]4(12) | [1]3(11) | [1]2(10) | [1]1(9) | [1]0(8) | [0]7 | [0]6 | [0]5 | [0]4 | [0]3 | [0]2 | [0]1 | [0]0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
那么我们就需要令 char[0] = 0x08 ;
这里面使用到了3个数据 2
存储的数 , 0
要修改列表中的第几个数据, 0x08
数据新值 ;
其中 2
存储的数 是已知数据。 0 和 0x08 均是通过计算获得的。下面我们就来看看如何计算出 这两个未知数据;
求0
要修改列表中的第几个数据
我们已经知道 char 的大小是 1byte 也就是 8bit。在Bitmap里共可以存储8个数据。那么每 8 个数据就需要占用列表中的一个元素。所以 要修改列表中的第几个数据 可以用下面的公式计算得出:
要
修
改
列
表
中
的
第
几
个
数
据
=
向
下
取
整
(
存
储
的
数
/
元
素
的
b
i
t
大
小
)
要修改列表中的第几个数据 = 向下取整(存储的数 / 元素的bit大小)
要修改列表中的第几个数据=向下取整(存储的数/元素的bit大小)
也就是说 list_n = (int)(n / sizeof(char))
如果你的列表是int类型的列表 int[]
那么就是 list_n = (int)(n / sizeof(int))
那么这个时候一个元素就可以存储 32 个数据了。
求 0x08
数据新值
如果原先这个数据里面就有值 比如是 0x03 ,我们需要在 2 的位置 设置 1 .。 那么我们可以进行如下操作:
- 先获取值有2位置为1时的数据 :
0x01 << 2
也就是0x02
。 - 原有的数据 与 现有的数据 只要是 1 就设置成 1 . 也就是 或 操作:
0x03 | 0x01
也就是0x04
- 也就是执行下面的代码 char[0] |= ( 0x01 << n )
但是如果是要修改的是Bitmap呢?那么就需要下面的伪代码了:
把指定地方改成 1 :
c h a r [ ( i n t ) ( n / s i z e o f ( c h a r ) ) ] ∣ = ( 1 < < n ) char[(int)(n/sizeof(char))] |= ( 1 << n ) char[(int)(n/sizeof(char))]∣=(1<<n)
把指定地方改成 0 :
KaTeX parse error: Can't use function '\~' in math mode at position 32: …eof(char)] \&= \̲~̲~( 1 << n)
bitmap 实现思想
应用实例
下面给出几个应用的例子
判断QQ账号是否存在
QQ账号现最大位数是 10 位 ,理论账号数量可达 9,999,999,999 九十九亿… 个。假设现在QQ号大约已有 1,00,000,000 十亿 且最大号码小于 4,000,000,000 四十亿 。在用户注册账号时,需要判断他注册的账号是否存在。如果你需要设计这个程序,要求可以 快速判断账号是否存在 并且 数据与程序占用内存不超过 512 GB ,你会如何设计。
如果我们直接把QQ号直接用 unsigned long (32 位系统中大小 4个字节) 列表的形式 存储下所有QQ号,那么现在有 10亿个数那么当当只是数据就需要 :
1
,
000
,
000
,
000
∗
4
/
1024
/
1024
≈
3.7
T
1,000,000,000 * 4 / 1024 / 1024 \approx 3.7T
1,000,000,000∗4/1024/1024≈3.7T
这个不符合要求的,再加上需要对数据依次进行比对,如果列表是混乱的的话,就有可能需要进行 5千万以上的比对次数。这样也不能做到快速。如果是有序列表呢?有序列表确实利用算法(比如二分法)可以让查询速度变快,但是存储新数据与删除旧数据时,所需要的时长也会更长。
那么有没有两全其美的方法呢?有! 使用 BitMap :
如果我们使用 bitmap 下存储下所有可能出现的账号 ,共 4,000,000,000 个。通过对账号所在区域标志 1 true 有 , 0 false 没有 来存储这些数据 。那么内存只需要 :
4000000000
/
8
/
1024
/
1024
≈
476
G
4000000000 / 8 / 1024 / 1024 \approx 476G
4000000000/8/1024/1024≈476G
这符合要求,相比于
3.7
T
,
476
G
3.7T,476G
3.7T,476G可以说是小太多了。那么查询速度也是需要读取那位的0或者1即可。更新速度也会有很大的提升。
对一组不重复的数据排序
有一组不重复的混乱数据,共 5000 个 ,数字大小在 0 - 10000。 请设计一个程序将他们按从大到小的顺序重新排序。
这个正好满足 数据不重复 , 数据分布紧凑。我们就可以通过 bitmap 来实现 。 创建一个 10000bit 大小的 bitmap 。然后遍历这5000个需要重新排序的数据。把读到的数据对应的位置全部写成1。然后由大到小遍历bitmap,按读到1的顺序,把数据重新输出即可。
各种语言实现bitmap
C语言
#include <stdio.h>
#include <math.h>
class Bitmap {
unsigned char list[2000000] = {0};
public:
bool Get_bit( unsigned int num ){
return ( list[(int)(num/8)] & (0x01 << (num % 8) ) ) > 0 ? true : false ;
}
void Set_bit( unsigned int num ){
list[(int)(num/8)] |= (0x01 << (num % 8) ) ;
}
void Res_bit( unsigned int num ){
list[(int)(num/8)] &= ~(0x01 << (num % 8) ) ;
}
};
int main(){
//定义一个列表 Bitmap
Bitmap bitmap;
// 记录下 12
bitmap.Set_bit(12);
// 查询是否有12
printf("%d",bitmap.Get_bit(12));
}
联系作者
本文作者为 > 【谢玄.】 Mr-XieXuan < 于 2022/10/30/3:00 发布于 CSDN 。
GitHub: [ https://github.com/MR-XieXuan }
个人私站: [ https://main.mrxie.xyz/ ]