散列表-散列函数设计与散列冲突处理

- 1 min

数据结构与算法系列(二十二)

昨天我们分享了散列表的实现,对 PHPer 来说,应该对散列表很熟悉,因为我们每天用的数组就是基于散列表实现的。比如 $arr[‘test’] = 123 这段代码,PHP底层会将键值 test 通过散列函数转化为散列码,然后将 123 映射到这个散列码上。在不考虑哈希冲突的情况下,散列表查找、删除、插入的时间复杂度都是 O(1),非常高效。

要减少哈希冲突,提高散列表操作效率,设计一个优秀的散列函数至关重要,我们平时经常使用的 md5 函数就是一个散列函数,但是其实还有其他很多自定义的设计实现,要根据不同场景,设计不同的散列函数来减少散列冲突,而且散列函数本身也要很简单,否则执行散列函数本身会成为散列表的瓶颈。我们日常很少会自己去设计散列函数,但是做一些简单的了解还是有必要的。

通常有以下几种散列函数构造方法:

我们在上篇分享中提到,设计再好的散列函数也不能完全避免散列冲突,我们只能优化自己的实现让散列冲突尽可能少出现罢了,如果出现了散列冲突,该如何处理呢?下面给出一些思路:

散列函数设置合理,不能太过复杂,成为性能瓶颈;

设置装载因子阈值,支持动态扩容,装载因子阈值设置要充分权衡时间、空间复杂度;

如果一次性扩容耗时长,可采取分批扩容的策略,达到阈值后只申请空间,不搬移数据,以后每插入一条数据,搬移一个旧数据,最后逐步完成搬移,期间为了兼容新老散列表查询,可以先查新表,再查老表;

散列冲突解决办法:开发寻址法在数据量较小、装载因子小的时候(小于1)选用;链表法可以容忍装载因子大于1,适合存储大对象、大数据量的散列表,且更加灵活,支持更多优化策略

rss facebook twitter github gitlab youtube mail spotify lastfm instagram linkedin google google-plus pinterest medium vimeo stackoverflow reddit quora quora