散列表(中)

如何设计这样一个工业级散列表呢?我会从3个方面来考虑设计思路:

1.设计一个合适的散列函数;
2.定义装载因子阈值,并且设计动态扩容策略;
3.选择合适的散列冲突解决方法。

一、如何设计散列函数?

1.要尽可能让散列后的值随机且均匀分布,这样会尽可能减少散列冲突,即便冲突之后,分配到每个槽内的数据也比较均匀。
2.除此之外,散列函数的设计也不能太复杂,太复杂就会太耗时间,也会影响到散列表的性能。
3.常见的散列函数设计方法:直接寻址法、平方取中法、折叠法、随机数法等。

二、如何根据装载因子动态扩容?

如何设置装载因子阈值?

1.可以通过设置装载因子的阈值来控制是扩容还是缩容,支持动态扩容的散列表,插入数据的时间复杂度使用摊还分析法。
2.装载因子的阈值设置需要权衡时间复杂度和空间复杂度。如何权衡?如果内存空间不紧张,对执行效率要求很高,可以降低装载因子的阈值;相反,如果内存空间紧张,对执行效率要求又不高,可以增加装载因子的阈值。

如何避免低效扩容?分批扩容

1.分批扩容的插入操作:当有新数据要插入时,我们将数据插入新的散列表,并且从老的散列表中拿出一个数据放入新散列表。每次插入都重复上面的过程。这样插入操作就变得很快了
2.分批扩容的查询操作:先查新散列表,再查老散列表。
3.通过分批扩容的方式,任何情况下,插入一个数据的时间复杂度都是O(1)。


分批搬移数据

三、如何选择散列冲突解决方法?

  • 常见的2中方法:开放寻址法和链表法。
  • 大部分情况下,链表法更加普适。而且,我们还可以通过将链表法中的链表改造成其他动态查找数据结构,比如红黑树、跳表,来避免散列表时间复杂度退化成O(n),抵御散列冲突攻击。
  • 但是,对于小规模数据、装载因子不高的散列表,比较适合用开放寻址法。

分析Java的HashMap:

初始大小

hashMap默认的初始大小是16.

装载因子和动态扩容

最大装载因子默认为0.75,当hashMap中元素个数超过0.75*capacity(capacity表示散列表的容量)的时候,就会启动扩容。每次扩容都会扩容到原来的两倍。

散列冲突解决方法

使用单链表解决hash冲突,当链表长度大于8,将单链表转换成红黑树 , 当链表长度小于6,将红黑树退化为单链表。

散列函数

hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 如何设计散列函数 散列函数的设计的好坏,决定了散列冲突的概率大小,也直接决定了散列表的性能。 好的散列函数,应该有...
    wean_a23e阅读 3,793评论 0 0
  • 散列表(hash table)是实现字典操作的一种有效数据结构,尽管最坏情况下,散列表中的查找一个元素的时间与链表...
    Mrsunup阅读 5,229评论 0 2
  • 说明:该系列博客整理自《算法导论(原书第二版)》,但更偏重于实用,所以晦涩偏理论的内容未整理,请见谅。另外本人能力...
    黑夜0411阅读 5,289评论 0 2
  • 盛年不重来,一日难再晨 及时当勉励,岁月不待人 —...
    Sophie老师助教Tony阅读 2,833评论 0 1
  • 桌角摆了绿箩。 就那么一点绿呀,看着心里柔软,柔软到生出了袅袅的藤蔓。书与看书的人,便在一缕缕垂枝下,往更深的暖意...
    辛里阅读 4,597评论 43 33