上一章咱们讲了如何根据须要动态设置hash表
的大小,在第四章中,咱们使用了双重哈希
来解决hash表
的碰撞,其实解决方法有不少,这一章咱们来介绍下其余方法。segmentfault
本章将介绍两种解决hash表
碰撞的方法:缓存
使用拉链法,每个bucket
都会包含一个连接表
,当发生碰撞
时,就会将该记录插入在该位置的连接表
后面,步骤以下:函数
hash函数
获取到要插入的位置,若是该位置是空的,就直接插入,若是该位置不是空的,就插入在连接表
的后面hash函数
获取到key
对应的位置,遍历连接表
,判断key
是否是搜索的key
,若是是,则返回value
,不然返回NULL
hash函数
获取到key
对应的位置,遍历连接表
,找到须要删除的key
,若是找到,则将该key
对应的记录从连接表
中删除,若是连接表
中只有一条记录,则将该位置置为NULL
拉链法的优势是实现起来简单,可是空间利用率低。每一个记录必须存储指向连接表
中下一个记录的指针,若是没有记录,则指向NULL
,这种方法会浪费一些空间来存储额外的指针。性能
开放地址法能解决拉链法空间利用率低的问题,发生碰撞时,碰撞的记录将放置在hash表
中的其余bucket
中,存放的位置是根据预先肯定的规则选择的,以便在搜索记录时能够重复该规则,有以下几种规则:spa
当发生碰撞
时,就会递增索引,将记录插入在下一个可用的索引中,方法以下:指针
hash函数
找到插入的位置的索引,若是这个位置是空的,直接插入,若是不为空,就递增索引,直到找到索引指向的位置是空的为止,而后执行插入hash函数
找到搜索的记录的索引,每次递增索引,并比较索引指向的值是不是要搜索的值,若是索引指向的是空,则返回NULL
hash函数
找到删除的记录的索引,每次递增索引,直到找到要删除的那个key
后执行删除线性探测提供了良好的缓存性能,可是存在碰撞后遍历次数多的问题。将发生碰撞
的key
放入下一个可用的bucket
中可能致使后面插入记录也要日后插,就须要屡次迭代。code
二次探查法和先行探查相似,不一样的是,发生碰撞
后,咱们会将记录插入在以下的序列中:i, i + 1, i + 4, i + 9, i + 16, ...
,i
表明经过hash函数
获取到的索引,具体步骤以下:教程
hash函数
找到插入的索引,经过遍历上面的序列直到找到一个空的或已被删除的索引位置,执行插入hash函数
找到key
的索引,遍历上面的序列,将序列上的key
与搜索的key
对比,若是相等,则返回value
,不然返回NULL
碰撞链
上的,因此咱们不能直接删除该条记录,只能把它标记为已删除
二次探查法减小发生碰撞
后遍历的次数,而且仍然提供了不错的缓存性能。索引
hash
双重hash
旨在解决碰撞后遍历次数多的问题。使用两次hash函数
为插入的记录选择新的索引,这个索引会均匀的分布在整个表中,该方法虽然解决了上述问题,但也失去了缓存特性,双重hash
是实际项目中常见的冲突管理方法,也是咱们在本教程中实现的方法。get
上一章:设置hash表
大小