[数据结构]——位图原理及实现

位图

今天我们所介绍的数据结构叫做位图,在谈什么是位图之前我们先来看一道"非常简单的题":有40亿个无符号的整型数据,现在给定一个目标数字,判断这个数字是否在这40亿数据中。题目看起来确实非常简单,有的同学说直接遍历一遍不就ok了吗?还有的同学给出了更高效的查找方式就是将这些数字排序然后进行二分查找。但是,这是有问题的,问题并不在于你搜索这个数字的效率问题,而是你在遍历也好排序也罢,这些数字在内存中放的下么?

一个整型int就是4个字节,10亿个int差不多已经需要4G的内存了,40亿个int就是16G。所以这里方法行不通的根本原因实际上是内存不够,但是我们今天的讲的位图却能很好的帮助我们处理这个问题。

位图模型

既然根本原因是这些数据用int放不下,那么是否有更小的东西标记这些数字呢?没错,有的同学想到了,char只占一个字节或许能表示一个数字,但是随着数字位数的增多,依旧不可能使用一个字符表示一个数字,这就意味着小于4G内存还是不能解决这个问题。
其实说到这里,我们的问题就转化为如何使用更小的内存单元来标记一个数字,而在程序中我们最小的访问单位的bit位,所以现在我们一起来看使用比特位如何标记(映射)这些数据。
在这里插入图片描述
现在我们发现,4个字节本来只能存储一个int,而现在使用位图我们就存了(映射)32个数字,意味着16G/32约等于500m左右我们就能映射这些数据,那么这些数据是怎么映射到位图种的呢?接着看。

设计位图

为了方便,我们将位图用一个数组表示,让vector帮我们开辟一段连续的空间,我们只负责将数据设置或者移除就行。

class BitMap
{
public:
	BitMap(size_t range)
	{
		//右移5位相当于除以32,加1是因为小于32的数字如果与32相除则得到0
		_bitTable.resize((range >> 5) + 1);
	}
	
private:
	vector<int> _bitTable;
};

位图元素的设置

	void SetBit(size_t x)
	{
		size_t index = x >> 5;
		size_t num = x % 32;

		_bitTable[index] |= (1 << num);
	}

来看看为什么需要size_t index = x >> 5size_t num = x % 32两步操作:我们看看要映射5和32这俩个数
在这里插入图片描述
5表示防在第1个整型空间的第5位上,32则表示放在第2个整型空间第一位上。而**bitTable[index] |= (1 << num)**能保证把第num位上的数字设置为1,其余数字保持不变。

位图元素的移除

比较简单,需要知道的是**~(1 << num)**表示出了num位为0,其余位都为1.

	void RemoveBit(size_t x)
	{
		size_t index = x >> 5;
		size_t num = x % 32;

		_bitTable[index] &= ~(1 << num);
	}

位图元素的查找

这个没啥好说的,很简单,说到这里,你的位图也就实现完了,非常简单把

bool TestBit(size_t x)
	{
		size_t index = x >> 5;
		size_t num = x % 32;

		return _bitTable[index] & (1 << num);
	}

完整代码:

class BitMap
{
public:
	BitMap(size_t range)
	{
		_bitTable.resize((range >> 5) + 1);
	}

	//标识一个数字在位图中的位置
	void SetBit(size_t x)
	{
		size_t index = x >> 5;
		size_t num = x % 32;

		_bitTable[index] |= (1 << num);
	}

	//取消数字在位图当中的标识.
	void RemoveBit(size_t x)
	{
		size_t index = x >> 5;
		size_t num = x % 32;

		_bitTable[index] &= ~(1 << num);
	}


	bool TestBit(size_t x)
	{
		size_t index = x >> 5;
		size_t num = x % 32;

		return _bitTable[index] & (1 << num);
	}

private:
	vector<int> _bitTable;
};

拓展

现在将问题修改为让你寻找出40亿个数据中出现过两次的数据,此时我们就需要使用两位来标记同一个数据了。

N位位图的实现如下:

class NBitMap
{
public:
	NBitMap(size_t range)
	{
		_bitTable.resize((range >> 4) + 1);
	}

	void SetBit(size_t x)
	{
		size_t index = x >> 4;
		size_t num = x % 16;
		num *= 2;

		bool first = _bitTable[index] & (1 << num);
		bool second = _bitTable[index] & (1 << (num + 1));

		if (!(first && second))
		{
			_bitTable[index] += (1 << num);
		}
	}

	bool TestBit(size_t x)
	{
		size_t index = x >> 4;
		size_t num = x % 16;
		num *= 2;

		return (_bitTable[index] >> num) & 0x03;
	}

private:
	vector<int> _bitTable;
};

关于位图的讲解就到这里,现在我让你查找10亿个字符串中出现一次的那个字符串,有的同学丝毫不犹豫就要用我们使用的位图,但是仔细思考,我们这里的位图只是可以映射数字类型的数据,变成字符串以及其他文件好像就不再那么得心应手了,别急,聪明的大佬们又想到了一种骚东西叫做布隆过滤器,那么布隆过滤器是什么呢?请看下篇博客哦。

  • 76
    点赞
  • 220
    收藏
    觉得还不错? 一键收藏
  • 15
    评论
0; } ``` 程序运行时,会显示一个菜单,让用户选择不同的功能,例如: ``` 1位图索引(Bitmap Index)是一种特殊类型的索引,它使用位图存储数据。对于列中的每个不同值,位图索引都会创建一个位图,其中每个位对应于表中的. 添加学生信息 2. 显示所有学生信息 3. 按总分排序 4. 按DataStructure成绩排序一行。如果值出现在行中,则相应的位设置为1,否则为0。通过使用位图索 5. 按C成绩排序 6. 查找某一C成绩的记录 0. 退出程序 请选择操作引,可以快速地定位符合特定查询条件的行。 位图索引通常用于低基数(即不同值数量较少)列上,例如性别列等。在这种情况下,位图索引可以(输入数字): ``` 用户可以根据需要选择不同的功能,程序会根据用户的选择执行相应极大地减少磁盘 I/O 操作,提高查询性能。但是,如果列具有高基数(即的操作,例如: ``` 请输入学号:001 请输入姓名:张三 请输入DataStructure成绩:90 请输入不同值数量较多),则位图索引的效率可能会降低。 在 Oracle 数据库中,可以使用 CREATEC成绩:80 添加成功 请选择操作(输入数字):2 学号 姓名 DataStructure C 总分 BITMAP INDEX 语句创建位图索引,例如: ``` CREATE BITMAP INDEX idx_gender ON employees(gender); ``` 001 张三 90.0 80.0 170.0 请选择操作(输入数字):3 按总这将在 employees 表的 gender 列上创建一个位图索引。在查询中使用位图索引时,可以使用 BIT分排序结果: 学号 姓名 DataStructure C 总分 001 张三 90.0 80.0 MAP AND、BITMAP OR、BITMAP NOT 等操作符组合多个位图索引,以获得更高效的查询性能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值