- 1. unordered系列关联式
- 2. C++下“hash“的简单模拟实现以及实现中遇见的问题
- 3.哈希的应用
- 位图
- 位图模拟实现
- 布隆过滤器
- Set
- N的大小
- 删除
- 需求
- 布隆过滤器代码
- 4.海量数据处理面试题
- 位图应用
- 布隆过滤器
- 哈希切割
区别:
1.unordered_XXX 遍历不按key排序,命名体现
2.unordered_XXX单向迭代器
3.unordered_XXX综合效率略胜map和set
数据重复量比较多的时候,unordered_set更好
重复量比较少的时候,有序的时候,set比较好
链接: C++下“hash“的简单模拟实现以及实现中遇见的问题
3.哈希的应用 位图给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这40亿个数中。
解决:
- 遍历,时间复杂度O(N)
- 排序(O(NlogN)),利用二分查找: logN
- 位图解决
数据是否在给定的整形数据中,结果是在或者不在,刚好是两种状态,那么可以使用一个二进制比特位来代表数据是否存在的信息,如果二进制比特位为1,代表存在,为0代表不存在。比如:
位图概念
所谓位图,就是用每一位来存放某种状态,适用于海量数据,数据无重复的场景。通常是用来判断某个数据存不存在的
优点:节省空间,效率高
局限性:正能处理整数
#pragma once #include布隆过滤器namespace sakeww { template class bitset { public: bitset() { _bits.resize(N / 8 + 1, 0); } void set(size_t x) { size_t i = x / 8; size_t j = x % 8; _bits[i] |= (1 << j);//将0-》1 } void reset(size_t x) { size_t i = x / 8; size_t j = x % 8; _bits[i] &= (~(1 << j));//将1-》0 } bool test(size_t x)//查找 { size_t i = x / 8; size_t j = x % 8; return _bits[i] & (1 << j);//判断1还是0 } private: std::vector _bits; }; void test_bitset() { bitset<100> bs; bs.set(5); bs.set(4); bs.set(10); bs.set(20); cout << bs.test(5) << endl; cout << bs.test(4) << endl; cout << bs.test(10) << endl; cout << bs.test(20) << endl; cout << bs.test(21) << endl; cout << bs.test(6) << endl << endl; bs.reset(20); bs.reset(10); bs.reset(5); cout << bs.test(5) << endl; cout << bs.test(4) << endl; cout << bs.test(10) << endl; cout << bs.test(20) << endl; cout << bs.test(21) << endl; cout << bs.test(6) << endl; //bitset<0xffffffff> bs; } }
一个值映射多个位置
Set#pragma once #includeN的大小 删除using namespace std; #include #include struct BKDRHash { size_t operator()(const string& s) { // BKDR size_t value = 0; for (auto ch : s) { value *= 31; value += ch; } return value; } }; struct APHash { size_t operator()(const string& s) { size_t hash = 0; for (long i = 0; i < s.size(); i++) { if ((i & 1) == 0) { hash ^= ((hash << 7) ^ s[i] ^ (hash >> 3)); } else { hash ^= (~((hash << 11) ^ s[i] ^ (hash >> 5))); } } return hash; } }; struct DJBHash { size_t operator()(const string& s) { size_t hash = 5381; for (auto ch : s) { hash += (hash << 5) + ch; } return hash; } }; template class BloomFilter { public: void Set(const K& key) { //测试数值 //cout << HashFunc1()(key) << endl; //cout << HashFunc2()(key) << endl; //cout << HashFunc3()(key) << endl; //cout << endl; size_t index1 = HashFunc1()(key); size_t index2 = HashFunc2()(key); size_t index3 = HashFunc3()(key); _bs.set(index1); _bs.set(index2); _bs.set(index3); } private: bitset _bs; }; void test1() { BloomFilter<100> bf; bf.Set("aadd"); bf.Set("abcd"); bf.Set("acbd"); }
不能删除
如果硬要?
每个标记位使用多个比特位,存储引用计数(有几个值映射了当前位置)
也可以对特殊位置增加map存储,
支持删除,整体而言消耗空间变多了,布隆过滤器的优势下降了
数据量大,节省空间,允许误判
举个栗子:
昵称:注册信息的时候,判断昵称有没有人用过?
可以将数据库中的昵称,放到布隆过滤器
在是存在误判的,不在是不存在误判的
昵称判断在的情况下,可以再在数据库中查一下,这样对用户更加负责
垃圾邮件:
每个邮件都有一个发件地址,可以制作一个黑名单,
可以对垃圾邮件进行标记,将其放进布隆过滤器中,
下一封邮件,如果不在布隆过滤器中,那么一定不是垃圾邮件。
利用布隆过滤器减少磁盘IO或者网络请求,因为一旦一个值必定不存在的话,我们可以不用进行后续昂贵的查询请求
布隆过滤器代码#pragma once #include4.海量数据处理面试题 位图应用using namespace std; #include #include struct BKDRHash { size_t operator()(const string& s) { // BKDR size_t value = 0; for (auto ch : s) { value *= 31; value += ch; } return value; } }; struct APHash { size_t operator()(const string& s) { size_t hash = 0; for (long i = 0; i < s.size(); i++) { if ((i & 1) == 0) { hash ^= ((hash << 7) ^ s[i] ^ (hash >> 3)); } else { hash ^= (~((hash << 11) ^ s[i] ^ (hash >> 5))); } } return hash; } }; struct DJBHash { size_t operator()(const string& s) { size_t hash = 5381; for (auto ch : s) { hash += (hash << 5) + ch; } return hash; } }; template class BloomFilter { public: void Set(const K& key) { //测试数值 //cout << HashFunc1()(key) << endl; //cout << HashFunc2()(key) << endl; //cout << HashFunc3()(key) << endl; //cout << endl; size_t len = X * N; size_t index1 = HashFunc1()(key) % len; size_t index2 = HashFunc2()(key) % len; size_t index3 = HashFunc3()(key) % len; //cout << index1 << endl; //cout << index2 << endl; //cout << index3 << endl; //cout << endl; _bs.set(index1); _bs.set(index2); _bs.set(index3); } bool Test(const K& key) { size_t len = X * N; size_t index1 = HashFunc1()(key) % len; if (_bs.test(index1) == false) return false; size_t index2 = HashFunc2()(key) % len; if (_bs.test(index2) == false) return false; size_t index3 = HashFunc3()(key) % len; if (_bs.test(index3) == false) return false; return true;//存在误判 } //不能删除 //void Reset(const K& key); private: bitset _bs; }; void test1() { BloomFilter<100> bf; bf.Set("aadd"); bf.Set("abcd"); bf.Set("acbd"); }
- 给定100亿个整数,设计算法找到只出现一次的整数?
templateclass TwoBitSet { public: void Set(size_t x) { if (!_bs1.test(x) && !_bs2.test(x)) // 00 -> 01 { _bs2.set(x); } else if (!_bs1.test(x) && _bs2.test(x)) // 01 -> 10 { _bs1.set(x); _bs2.reset(x); } // 10 表示已经出现2次或以上,不用处理 } void PrintOnceNum() { for (size_t i = 0; i < N; ++i) { if (!_bs1.test(i) && _bs2.test(i)) // 01 { cout << i << endl; } } } private: sakeww::bitset _bs1; sakeww::bitset _bs2; }; void TestTwoBitSet() { int a[] = { 1,2,3,4,5,6,7,8,9,4,5,6,3,2,1,6}; TwoBitSet<100> bs; for (auto e : a) { bs.Set(e); } bs.PrintOnceNum(); }
- 给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集?
一个文件中整数,set到一个位图,读取第二个文件中的整数判读在不在位图,再就是交集,不在就不是
缺陷:交集会把重复值找出来,多次出现
改进:
一个文件中整数,set到一个位图bs1,另一个文件中整数,set到一个位图bs2
a.遍历bs2中值,看在不在bs1,在就是交集
b.bs1中的值依次跟bs2中的值与一下,再去看与完是1的位置值就是交集
- 位图应用变形:1个文件有100亿个int,1G内存,设计算法找到出现次数不超过2次的所有整数
用两个位图
出现0:00
出现1:01
出现2:10
出现3及以上:11
给两个文件,分别有100亿个query,我们只有1G内存,如何找到两个文件交集?分别给出精确算法和近似算法?
近似算法:将100亿个query放进一个布隆过滤器,另外一个文件在就是交集,不在就不是;
交集里面:存在不是交集的query
精确算法:
(query是字符串,比如是SQL语句,是网络请求url)
假设每个quert是10byte,100亿个query需要100G左右存储空间
哈希切分:
ai和bi小文件找交集就可以
A和B中相同的query一定进入编号相同的小文件
为什么是200?
取决于你有多少内存和 需要多少存储空间,
平均下来100G 切200份,一份500MB ,我们有1G内存,应该是够用的
但是有一些小文件比较大,例如:A123 有1G以上,那么就需要再切分一次,或者切的更多点(增大200)
这个方法还有什么问题?
如果Ai和Bi都太大,超过内存,可以考虑换个哈希算法,在切分一次。
给一个超过100G大小的log file, log中存着IP地址, 设计算法找到出现次数最多的IP地址? 与上题条件相同,如何找到top K的IP?
哈希切分:
依次读取ip,i=BKDRHash(ip)%100
i是多少,ip就进入对应的编号i小文件中
相同的ip一定进入了同一个小文件,那么我们使用map统计一个小文件中的ip的次数,就是它准确的次数
pair
出现次数最多的,top K的IP?
priority_queue



