一、背景
(1)在使⽤word⽂档时,word如何判断某个单词是否拼写正确?
(2)⽹络爬⾍程序,怎么让它不去爬相同的url⻚⾯?允许有误差
(3) 垃圾邮件(短信)过滤算法如何设计?允许有误差 公安办案时,
(4)如何判断某嫌疑⼈是否在⽹逃名单中?控制误差 假阳率
(5)缓存穿透问题如何解决?允许有误差
以上几个问题都是需拿着一个字符串去海量数据源当中去查找和对比,来确定这个字符串是否存在。
那么,从海量数据中查询某字符串是否存在,我们应该用那些数据结构呢?,接下我们分析以下各种数据类型的优缺点。
二、set和map
1、c++标准库(STL)中的set和map结构都是采⽤红⿊树实现的,它增删改查的时间复杂度是 ;
2、图结构示例:
3、对于严格平衡⼆叉搜索树(AVL),100w条数据组成的红⿊树,如果红黑树的key都是int型的整数值时,只需要⽐较20次就能找到该值;对于 10亿条数据只需要⽐较30次就能找到该数据,整数之间比较速度是相当快的,没有啥问题。
但是,像上面5个背景中,对应的红黑树的key都是字符串,特别是像第(2)个,key为url,对于 10亿条数据,比较30此,也是很耗时的。
总结:当要从海量数据中查询某字符串是否存在用set和map的
优点:存储效率高,访问速度高效。
缺点:对于数据量大且查询字符串比较长且字符串相似时,将会是噩梦。
三、unordered_map
1、c++标准库(STL)中的unordered_map<string, bool>是采⽤hashtable实现的;
2、构成:数组+hash函数;
3、它是将字符串通过hash函数⽣成⼀个整数再映射到数组当中;它增删改查的时间复杂度是o(1);
4、图结构示例:
5、unordered_map数据结构的使用过程:对unordered_map<string, bool>中的key,也就是把string利用hash函数进行哈希,生成一个整数(一般生成的是64位的整数),再对数组长度取余,然后映射到数组中,也就是把key和value存储到数组中,
6、hash函数的作用:当插入字符串和查找字符串时,避免了字符串的比较,节省了时间。
7、如何选取hash函数:
(1)选取计算速度快;
(2)哈希相似字符串能保证强随机分布性(防碰撞);
8、常用的hash函数:
murmurhash1,murmurhash2,murmurhash3,siphash(redis6.0当中使⽤,rust等⼤多数 语⾔选⽤的hash算法来实现hashmap),cityhash都具备强随机分布性;
测试地址:https://github.com/aappleby/smhasher
9、负载因⼦:数组存储元素的个数/数组⻓度;负载因⼦越⼩,冲突越⼩;负载因⼦越⼤,冲突越⼤;
10、hash冲突解决⽅案:
hash函数⼀般返回的是64位整数,将多个⼤数映射到⼀个⼩数组中,必然会产⽣冲突;如上图,可能多个字符串被哈希后,多个字符串的key和value都存在插在的数组中同一个位置,这种情况需要以下2个方法解决
(1)链表法:引⼊链表来处理哈希冲突;也就是将冲突元素⽤链表链接起来;这也是常⽤的处理冲突的⽅ 式;但是可能出现⼀种极端情况,冲突元素⽐较多,该冲突链表过⻓,这个 时候可以将这个链 表转换为红⿊树;由原来链表时间复杂度 o(n) 转换为红⿊树时间复杂度 ;那么判 断该链表过⻓的依据是多少?可以采⽤超过256(经验值)个节点的时候将 链表结构转换为红 ⿊树结构;
(2)开放寻址法:将所有的元素都存放在哈希表的数组中,不使⽤额外的数据结构;⼀般使⽤线性探查的思路解 决;
1). 当插⼊新元素的时,使⽤哈希函数在哈希表中定位元素位置;
2). 检查数组中该槽位索引是否存在元素。如果该槽位为空,则插⼊,否则3;
3). 在 2 检测的槽位索引上加⼀定步⻓接着检查2;
加⼀定步⻓分为以下⼏种:
1. i+1,i+2,i+3,i+4 ... i+n
2. i- ,i+ ,i- ,1+ ...
这两种都会导致同类hash聚集;也就是近似值它的hash值也近似,那么它的数组槽位也靠 近,形成hash聚集;第⼀种同类聚集冲突在前,第⼆种只是将聚集冲突延后; 另外还可以使⽤双重哈希来解决上⾯出现hash聚集现象:
在.net HashTable类的hash函数Hk定义如下: Hk(key) = [GetHash(key) + k * (1 + (((GetHash(key) >> 5) + 1) % (hashsize – 1)))] % hashsize 在此 (1 + (((GetHash(key) >> 5) + 1) % (hashsize – 1))) 与 hashsize 互为素数(两数互为素数表示两者没有共同的质因⼦); 执⾏了 hashsize 次探查后,哈希表中的每⼀个位置都有且只有⼀次被访问到,也就是 说,对于给定的 key,对哈希表中的同⼀位置不会同时使⽤ Hi 和 Hj;
同样的hashtable中节点存储了key和val,hashtable并没有要求key的⼤⼩顺序,我们同样可以修 改代码让插⼊存在的数据变成修改操作;
总结:unordered_map优点和缺点
优点:访问速度更快;不需要进⾏字符串⽐较;
缺点:需要引⼊策略避免冲突,存储效率不⾼;空间换时间;
总结:红⿊树和hashtable都不能解决海量数据问题,它们都需要存储具体字符串,如果数据量⼤,提供 不了⼏百G的内存;所以需要尝试探寻不存储key的⽅案,并且拥有hashtable的优点(不需要⽐较 字符串),所以就会用到布隆过滤器,下一节介绍布隆过滤器。