✨前言✨
📘 博客主页:to Keep博客主页
🙆欢迎关注,👍点赞,📝留言评论
⏳首发时间:2024年5月17日
📨 博主码云地址:博主码云地址
📕参考书籍:《C++ Primer》《C++编程规范》
📢编程练习:牛客网+力扣网
**由于博主目前也是处于一个学习的状态,如有讲的不对的地方,请一定联系我予以改正!!!
1 位图的应用
我们先来了解一下腾讯的经典面试题
给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这40亿个数中。
想到快速查找,首先我们想到的就是哈希表,但是哈希表对于如此海量的数据,有那么大内存放的下吗?我们可以粗略的估计一下我们所需要的内存大小!首先,一个int是4个字节,40亿个数字就是160亿字节,1G大约是10亿字节,也就是说,40亿个数字就要16G!显然这些数据放到内存中就很大了!于是位图就有了应用场景了!
可以明确的一点就是,对于数据就两种情况,那就是在和不在两种场景,也就是0和1就可以表示了,那么我们不就可以使用比特位来表示一个数字是否存在了!也就是说40亿个数字,只需要用40亿个比特位来判断就行了,也就相当于5亿多个字节就搞定,也就是说花费0.5G就能搞定了!
2 位图的实现
2.1 位图的实现原理
位图的实现原理其实很简单,就是利用vector去帮我们开辟一段连续整形的内存空间,结合左移与右移的相关操作,对比特位进行操作。比特位为1表示当前数字存在,0表示不存在
2.2 左移与右移
在位图中,我们主要是对比特位进行操作,所以我们这里需要简单回顾一下左移与右移操作!左移就是由低位向高位移动,右移就是由高位向低位移动!万万不可以简单理解为向左边移动,向右边移动!如下图所示就是一个例子,右边才是高位:
假设我们要想表示数字56放到对应的比特位上,我们就要确定56在那个位置!所以我们先利用i = 56/32表示56在vetcor的第几个位置,j =56%32就可以确定在当前区间的那个位置了,也就是对应的在那个比特位上!然后我们再利用vector[i] |= (1<<j)就可以把56这个数据放到对应的位置上了!
2.3 位图重要函数的实现
构造函数,需要注意的是位图开多大空间,取决于数据的大小,这里是int类型,所以是除以32
MyBitSet()
{
//有N个数字,转化成开多少个整形字节的顺序表
v1.resize(N/32+1,0);
}
set函数
void set(size_t x)
{
size_t i = x / 32;
size_t j = x % 32;
//不改变其他比特位,只把当前要处理的数字放进对应的比特位
v1[i] |= (1 << j);
}
reset函数
void reset(size_t x)
{
size_t i = x / 32;
size_t j = x % 32;
//不改变其他比特位,把当前处理的数字的比特位置为0
v1[i] &= ~(1 << j);
}
test函数
bool test(size_t x)
{
size_t i = x / 32;
size_t j = x % 32;
//判断当前数字对应的比特为是0还是1
return v[i] & (1 << j);
}
3 位图的总结
也就是说位图对于这种海量数据(只能解决整型的)具有明显的优势所在,本质也是和哈希一样,通过建立对应的映射关系,从而实现快速的查找与修改!还有一些经典的面试题,如下所示:
- 给定100亿个整数,设计算法找到只出现一次的整数?
- 给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集?
- 1个文件有100亿个int,1G内存,设计算法找到出现次数不超过2次的所有整数
对于上述面试题的实现代码思路:位图的实现
其实也是利用位图进行解决,这里是1G内存,结合上面所说的,可以开辟两个整形的位图,但是如果要求512M,只能开一个位图又该如何进行处理呢?当然必须利用我们的切分思想,例如将数据分为0-2^31为一段,第二段开始用后面的数字开始减去2的31次方,又映射到位图中,完成相对映射来判断就可以了!