【C++高阶(六)】哈希的应用--位图&布隆过滤器
💓博主CSDN主页:杭电码农-NEO💓
⏩专栏分类:C++从入门到精通⏪
🚚代码仓库:NEO的学习日记🚚
🌹关注我🫵带你学习C++
🔝🔝
哈希的应用
- 1. 前言
- 2. 位图的概念以及定义
- 3. 位图的模拟实现
- 4. 布隆过滤器的概念以及定义
- 5. 布隆过滤器模拟实现(一)
- 6. 布隆过滤器模拟实现(二)
- 7. 处理海量数据的面试题
- 8. 总结
1. 前言
哈希最常用的应用是unordered
系列的容器,但是当面对海量数据
如100亿个数据中找有没有100这
个数时,使用无序容器的话内存放不下
所以哈希思想还有别的更重要的应用!
本章重点:
本篇文章着重讲解哈希的应用的
两个容器,一个是位图,一个是布隆
过滤器,并且模拟实现它们.最后会
讲解如何使用这两个容器来解决一
些海量数据的面试题问题
2. 位图的概念以及定义
请先看一道海量数据的面试题:
如果要使用unordered_set来解决
40亿个整数,一个整数占4四节,
总共大约占16个G的内存空间
并且set容器中不止有整型数据,还有
其他的数据,所以不能用set!
而一个数在或不在可以用1/0来表示
也就是说其实只需要一个比特位就可
以知道一个数在不在其中.
于是位图横空出世!
位图概念:
所谓位图,就是用每一位来存放某种状态,适用于海量数据,数据无重复的场景。通常是用来判断某个数据存不存在的
举例说明:
判断1~22中哪些数据是存在的
只需要用三个整型也就是24个
比特位的空间,同理,40亿个数据
也用不着16G的内存,使用0.5G
内存的位图即可判断一个数在不在!
3. 位图的模拟实现
先来看看库中实现的位图:
模板参数N代表位图的大小
位图有三个主要的接口函数:
- set: 将一个数据放入位图中
- reset:将一个数据从位图中删掉
- test:检测一个数据在不在位图中
位图本身就是一段连续的空间
所以用char类型数组来充当位图的
基本结构是很符合情况的!
先将位图框架写出来:
template//N是所有数中的最大值 class bit_set { public: bit_set() { _bit.resize(N / 8 + 1, 0); } void set(size_t x)//将第x位变成1 {} void reset(size_t x)//将第x位由1变0 {} bool test(size_t x) {} private: vector _bit; };
在写set,reset等函数时,要先清除一点,
那就是char类型的数组一个元素有八个
比特位,所以我们需要确定两个位置:
一是此数据在哪一个数组元素中
二是此数据对应此元素的第几个比特位
下面我们画个图来推导一下公式:
现在已经能准确的找到这个比特位了
那么怎样将这个比特位变成0/1并且
不会影响到其他的比特位呢?下面分享
两个很巧妙的方法,请大家细细品尝:
template//N是所有数中的最大值 class bit_set { public: bit_set() { _bit.resize(N / 8 + 1, 0); } void set(size_t x)//将第x位变成1 { //x/8->在第几个char //x%8->在这个char的第几个比特位 size_t i = x / 8; size_t j = x % 8; _bit[i] |= (1 size_t i = x / 8; size_t j = x % 8; _bit[i] &= ~(1 size_t i = x / 8; size_t j = x % 8; return _bit[i] & (1 size_t operator()(const string& key) { size_t val = 0; for (auto ch : key) { val *= 131; val += ch; } return val; } }; struct HashAP { size_t operator()(const string& key) { size_t hash = 0; for (size_t i = 0; i set(hash3); } bool test(const K& key) { size_t hash1 = Hash1()(key) % (_ratio * N); if (!_bits->test(hash1)) return false; // 准确的 size_t hash2 = Hash2()(key) % (_ratio * N); if (!_bits->test(hash2)) return false; // 准确的 size_t hash3 = Hash3()(key) % (_ratio * N); if (!_bits->test(hash3)) return false; // 准确的 return true; // 可能存在误判 } void reset(const K& key)//支持删除操作的话,可能会把其他数据对应的映射值删除 {} private: const static size_t _ratio = 5;//开的空间越大,误判率越小 std::bitset* _bits = new std::bitset;//标准库中的位图是在栈上开辟的静态数组,过大会栈溢出 };
6. 布隆过滤器模拟实现(二)
布隆过滤器的查找是一个很玄幻的过程:
分别计算每个哈希值对应的比特位置存储的是否为零,只要有一个为零,代表该元素一定不在哈希表中,否则可能在哈希表中
因为哈希函数可能存在冲突的原因,如下:
所以我们得出一个结论:
- 布隆过滤器说一个元素存在,那它可能存在
- 布隆过滤器说一个元素不在,那它一定不在
布隆过滤器的删除操作:
如果你理解了上面的内容,你一定能
明白布隆过滤器是不支持删除的,因为
删除一个关键字时可能将其他的关键字
的一部分也给删除了,因为一个bit位
只能存储一个二进制信息!
7. 处理海量数据的面试题
海量数据的处理,有对位图的应用
也有对布隆过滤器的应用一步一步解析
位图的应用:
- 给100亿个整数,设法找到只出现一次的整数?
- 给两个文件,分别有100亿个整数,只有1G内存,如何找到两个文件交集?
- 位图应用变形:一个文件有100亿个int,1G内存,设法找到出现次数不超过2次的所有整数
布隆过滤器的应用:
- 给两个文件,分别有100亿个query,我们只有1G内存,如何找到两个文件交集?分别给出精确算法和近似算法
- 如何扩展BloomFilter使得它支持删除元素的操作
这些问题大家可以下来想一想,有什么问题欢迎私信
8. 总结
讲到这里,哈希的所有内容就已经
讲完了,所以无脑哈希无脑哈希,
但实际上要学好哈希还真得费点脑子
海量数据得处理问题在面试时也是
经常问的,希望同学们好好学扎实!
🔎 下期预告:C++11新改动🔍