位图、布隆过滤器和哈希切割
哈希相关的内容参见哈希以及闭散列和开散列
一、位图
1、概念
- 位图是一种基于位操作的数据结构,用于表示一组元素的集合信息。它通常是一个元素值仅包含0和1的数组,其中每个元素(或位)对应集合中的一个元素。当集合中对应元素存在时,对应位的值为1;不存在时,对应位的值为0。
- 位图常用于判断某个元素是否属于某个集合,或者对多个集合做交集、并集或差集等集合运算。例如,在处理大数据集时,可以使用位图来高效地找出只出现一次的整数、找到两个大文件的交集等。
2、示意图
二、位图的简单实现
1、位图类
(1)代码
template<size_t N>
class BitSet
{
public:
BitSet()
{
_bs.resize(N / 32 + 1, 0);
}
private:
std::vector<int> _bs;
};
(2)说明
- 因为位图各个元素代表的内容只有存在与不存在。所以,使用一个比特位(0与1)进行标记即可。
- 位图需要一个连续的空间,则使用vector。
- vector中元素的类型可以是自定义的,这里为了方便使用int,它是4个字节,即32比特位。所以,初始化时需要进行相应的处理。
2、设置函数
(1)代码
void set(size_t num)
{
int i = num / 32;
int j = num % 32;
_bs[i] |= 1 << j;
}
void reset(size_t num)
{
int i = num / 32;
int j = num % 32;
_bs[i] &= ~(1 << j);
}
bool test(size_t num)
{
int i = num / 32;
int j = num % 32;
return _bs[i] & (1 << j);
}
(2)说明
- 因为位图需要使用的是特定比特位上的内容,所以,使用移位操作进行处理。
- 设置和取消设置是要修改位图的内容。所以,它们最后需要进行赋值操作,而检测则不需要。
三、应用
1、题目一
(1)题目
- 给40亿个不重复的无符号整数,并且是没排序的。给一个无符号整数,如何快速判断这个数是否在这40亿个数中?
(2)解法
- 因为40亿个无符号整数(4字节)的存储大约需要16GB内存,而内存中给不了这么大的连续空间。采用位图的方式,每个值映射一个比特位,42亿多bite也才占用0.5GB左右内存。
- 使用位图将给出的40亿个无符号整数映射到位图中并将其值置为1,当给出一个无符号整数进行检测时,直接映射到位图中,判断其值是否为1,如果为1,说明其在40亿个数中,否则则不在。
2、题目二
(1)题目
- 给定100亿个整数,设计算法找到只出现一次的整数。
(2)解法
- 因为在给定的100亿个整数中,整数的情况有三种,分别是出现零次、一次和两次及以上,则可以使用两个位图变量进行记录。
- 当数值未出现时,两个位图变量内该映射位置的值都为0;当数值出现一次时,第一个位图变量内该映射位置的值为0,第二个位图变量内该映射位置的值为1;当数值出现两次及以上时,第一个位图变量内该映射位置的值为1,第二个位图变量内该映射位置的值为0。
- 当检测出现一次的数值时,判断该数值映射到两个位图变量的地址处的值是哪种情况即可。
(3)示意图
3、题目三
(1)题目
- 给两个文件,文件内分别有100亿个整数,而我们只有1G内存,如何找到两个文件交集?
(2)解法
- 将两个文件内的数值分别映射到一个位图中,当数值映射到两个位图中的地址处的值都表示存在,则是交集。
(3)示意图
四、布隆过滤器
1、概念
- 布隆过滤器是由布隆(Burton Howard Bloom)在1970年提出的一种紧凑型的、比较巧妙的概率型数据结构,特点是高效地插入和查询,可以用来表示某样东西一定不存在或者可能存在。
- 布隆过滤器是用多个哈希函数,将一个数据映射到位图结构中。此种方式不仅可以提升查询效率,也可以节省大量的内存空间。
2、示意图
五、布隆过滤器相关操作
1、插入数据
(1)操作
- 将一个欲插入的数据用多个哈希函数分别计算出一个值,将这些值映射到一个位图中,被映射到的位置的比特位值置为1。
(2)示意图
2、查找
(1)操作
- 分别用对应的哈希函数计算欲查找的元素,再查看这些哈希值对应的比特位置存储的值是否为零。只要其中有一个值为零,则说明该元素一定不在哈希表中,否则说明其可能在哈希表中。
- 如果布隆过滤器查找某个元素的结果是不存在,则该元素一定不存在;而如果结果表明该元素存在,则该元素可能存在,也可能不存在。因为布隆过滤器存在一定的误判。
(2)存在示意图
(3)不存在示意图
(4)误判示意图
3、删除
(1)操作
- 布隆过滤器不能直接支持删除操作,因为在删除一个元素时,可能会影响其他元素。例如,删除上图中的snow元素,如果直接将该元素所对应的二进制比特位置为0,则dragon元素也会被检测为不存在(删除),因为这两个元素在多个哈希函数计算出的哈希值对应的比特位上刚好有重叠的部分。
- 支持删除的方法为将布隆过滤器中的每个比特位扩展成一个小的计数器,插入元素时给对应的k个计数器(k个哈希函数计算出的哈希地址)加一。删除元素时,给对应的k个计数器减一,通过多占用几倍存储空间的代价来增加删除操作。
- 这种删除操作的缺陷为无法确认元素是否真正在布隆过滤器中和存在计数回绕。
六、布隆过滤器的优缺点
1、优点
- 增加和查询元素的时间复杂度为O(K),K为哈希函数的个数,一般比较小。即时间复杂度与数据量大小无关。
- 哈希函数相互之间没有关系,方便硬件并行运算。
- 布隆过滤器不需要存储元素本身,在某些对保密要求比较严格的场合有很大优势。
- 在能够承受一定误判的情况下,布隆过滤器与其他数据结构相比,具有很大的空间优势。
- 数据量很大时,布隆过滤器可以表示全集。
- 使用同一组散列函数的布隆过滤器可以进行交、并、差集的运算。
2、缺点
- 有误判率,即存在假阳性(False Position),不能准确判断元素是否在集合中。但可以再建立一个白名单,存储可能会产生误判的数据。
- 不能获取元素本身。
- 一般情况下不能从布隆过滤器中删除元素。
七、哈希切割
1、概念
- 哈希切割是将一个大文件,利用哈希的原理,采用哈希函数计算文件内数据的哈希值,再将产生哈希值相同或者相近的元素归为一类,依次将该文件分为若干个小文件。
2、操作
- 当文件太大而无法加载到内存中时,可以采用哈希切割,再将切割后的小文件加载到内存中处理。
- 如果切割后的小文件还是太大,则这个小文件有两种情况。其一为这个小文件中大多数都是某一个元素,则可以先插入到容器(如set等)中去重;其二为这个小文件中有很多不同的元素,则需要更换哈希函数,进行二次切割。
3、示意图
本文到这里就结束了,如有错误或者不清楚的地方欢迎评论或者私信
创作不易,如果觉得博主写得不错,请点赞、收藏加关注支持一下💕💕💕