必须了解的数据结构 BitMap 基础 应用 与 C语言实现

本文简介

本文介绍一下 数据结构 BitMap 和用多种语言BitMap的简单实现 。

bitmap 简介

数据结构 Bitmap 的核心思想就是 用 连续的bit来标志一个 可连续 数据 的有无 。因为他只需要存储数据的有无(布尔值)而不是数据,所以可以大大的降低存储空间。也可以大大加快查询的速度。

bitmap的优缺点

优点

  • 节省存储所需要的空间
  • 查询速度快,更新速度快。

缺点

  • 需要对所有的可能都进行存储
  • 数据不可重复
  • 数据越紧凑节省内存方面的优点越明显,如果数据过于松散内存甚至不如直接存储。

bitmap 的应用方向

利用bitmap对紧凑数据的占用空间小,操作速度快的特点,我们可以用在:

  • 大量数的快速查找与存储

利用数据的不可重复性,我们可以应用在:

  • 数据去重

利用数据的有序性,与操作速度快的特点,我们可以应用在

  • 对无序不重复数据进行排序

用程序实现 bitmap 的逻辑思想

如果我们需要存储 0 − 7 0-7 07 八个数字的有无,那么我们就需要一个 char 类型的数据。其中 每个位就代表这个数值的有无:
如果都没有就是 十六进制的 0x00 二进制如下:

76543210
00000000

如果现在我们需要需要表示 1 , 3 , 5 , 7 1 , 3 , 5 , 7 1357 四个数存在那么这个char的值就是:

76543210
10101010

如果我们需要存储的数据的数字不止 0 − 7 0-7 07 ,而是 0 − 15 0-15 015呢?那么我们就需要一个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
0000000000000000

如果我们需要表示 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
0000000000000100

那么我们就需要令 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 .。 那么我们可以进行如下操作:

  1. 先获取值有2位置为1时的数据 : 0x01 << 2 也就是 0x02
  2. 原有的数据 与 现有的数据 只要是 1 就设置成 1 . 也就是 或 操作: 0x03 | 0x01 也就是 0x04
  3. 也就是执行下面的代码 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,0004/1024/10243.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/1024476G
这符合要求,相比于 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 。

E-mail: [ Mr_Xie_@outlook.com ]
GitHub: [ https://github.com/MR-XieXuan }
个人私站: [ https://main.mrxie.xyz/ ]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

谢玄.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值