好久没执笔,准备鞭策下躺平的自己,将自己之前所研究的一些雕虫小技分享给各位,如有不合理的地方,还希望各位指正,谢谢!废话不多说,直接进入正题。
Hashing collision
维基百科中对hashing有这样一段定义:
Hashing is the process of transforming any given key into another value. This is usually represented by a shorter, fixed-length key and makes it easier to find.
简单的说,hashing就是通过一种算法将一系列字符串转换为特殊的key,通过这个key我们可以很容易找到对应的value。
但是现实生活中可能存在这样的情况,同一个key可能映射到相同的地址,这样就会导致hashing collision
一般会有如下几种的解决办法
- Open Addressing
- Double Hashing
- Separate Chaining
这里会大概介绍下2,3两种情况的实现办法
Setup
在开始前会有两个需要考虑的问题
-
我们设置0.6的扩容因子,达到后进行拷贝扩容,并且扩容大小为素数
Why prime?
由于数学的本质,关键字可能有某种规律,例如大量的等差数列,那么公差和模数不互质的时候发生碰撞的概率会变大,而用质数就可以很大程度上回避这个问题
这边使用二分法进行下一个素数的查找
-
hash function的设置
通过hash all来对字符串进行初步hash,数组情况下如果存在碰撞则进行对hash all和hash end的结果进行求和取模,链表情况下存在碰撞则进行链表遍历到对应位置进行插入
Double Hasing
通过两次hash转换,降低hash碰撞的概率,这边准备一个包含数组的结构体,结合hash function来进行构造将hash过后的value存入数组中。当存在哈希碰撞的时候,则进行re-hash;当数组长度超过0.6的因子,则进行扩容处理;最后通过查询构造好的hashTable来计算平均查找次数,具体步骤如下:
1.创建一个素数长度的数组
2.hash所有的字符串,存入hash表
3.当遇到hash collision时,进行Rehash,存入hash表
4.当数组长度超过60%进行扩容,容器大小保持素数长度,复制旧表所有字符串到新表
5.从hashTable进行搜索, 完美的hash查询次数为1
Hash insert
Capacity expand
Separate Chaining
结合链表降低hash碰撞的概率,和double hash不同的是结构体是存放指针数组,结合hash function来进行构造将hash过后的value存入。如果存在碰撞则进行链表插入;当数组长度超过0.6的因子,则进行扩容处理;最后通过查询构造好的hashTable来计算平均查找次数,具体步骤如下:
1.创建一个素数长度的指针数组
2.hash所有的字符串,存入hash表
3.当遇到hash collision时,通过链表存入hash表
4.当数组长度超过60%进行扩容,容器大小保持素数长度,复制旧表所有字符串到新表
5.从hashTable进行搜索, 完美的hash查询次数为1
Pointer&Array
A linked-list Approach
Hash insert
Capacity expand
类似使用数组的形式这里不再赘述
Average Time
Conclusion
可以看下最后实现效果,通过存入2W+的单词关键字形成hash表,搜索5K+的单词,最后平均查询结果为
double hash
separate chaining