数据结构与算法学习笔记(七)——散列表(一)

定义

1、散列表又叫hash table,哈希表。利用数组支持按照下标随机访问数据的特性,在数组上进行扩展得到的散列表。
关键字(key)通过散列函数得到散列值———在散列表中,以散列值为下标查找对应的值
2、通过散列函数计算出的散列值只能为非负整数,因为数组的下标是从0开始
3、key相同的话,生成的散列值也一定相同。key1=key2,hash(key1)=hash(key2)
4、key不同的话,生成的散列表也可能相同,此为散列冲突(哈希冲突)。这时在散列表中根据散列值查找到的数据还需要比较key值。注意:散列表中存储的是对象。
5、散列函数设计的不能太复杂,过于复杂的散列函数影响计算时间和性能。且散列函数生成的值要尽可能的随机且均匀的分布。

如何解决散列冲突

1、开放寻址法:若出现了散列冲突,则重新探测出一个新的空闲位置,将其插入。
探测方法有
<1>线性探测法:从当前散列冲突的位置依次向后一个一个寻找(到底的话再重头寻找),直至找到空闲位置。

查找:比较散列值对应的元素和要查找的元素,若相同则找到,否则继续向后查找,直至遇到空闲位置,则返回,表示不在散列表中。

删除:删除数据不会将该数据位置清空,而是标记为deleted,探测时遇到deleted还是继续往下。若置空,则会导致查找的时候算法错误。

<2>二次探测法:和线性探测法一样,只是线性探测法的步长为1,二次探测法的步长为2
<3>双重散列:使用一组散列函数,若第一个散列函数计算出了散列冲突,则使用下一个散列函数计算散列值。

2、链表法:将散列值相同的数据放在相同槽位对应的链表中,即用链表来存储相同散列值的数据。
在这里插入图片描述
<1>插入:通过散列值找到相应的散列槽位,再插入到该槽位对应的链表中。时间复杂度:O(1)
<2>查找:通过散列值找到相应的散列槽位,再遍历链表。时间复杂度:O(k),k为链表长度
<3>删除:通过散列值找到相应的散列槽位,再遍历链表找到要删除的数据,删除。时间复杂度:O(k)

3、开放寻址法和链表法的区别:
开放寻址法适用于数量较小,装载因子小的场景,如java中的ThreadLocalMap。
链表法适用于存储大对象,大数据量的散列表,且灵活多变,链表可以用红黑树、跳表代替。

4、装载因子
<1>当散列表中空闲位置越少,散列冲突的可能性也越大,因此需要保证散列表中空闲位置的数量。用装载因子表示空位的多少:
散列表的装载因子=填入表中的元素个数/散列表的长度
<2>装载因子过大时,可对散列表进行动态扩容。类似数组的扩容,申请容量扩大一倍的新数组,再将数据搬移到新数组中。若散列表进行扩容搬移数据,则需要重新计算散列值
<3>一次性扩容的缺点:空间消耗多,且扩容一次耗时大。
解决方案:先申请扩大一倍的空间,但是不搬移数据,当插入新数据时,插入到新空间中,再将一个原老空间的数据搬移到新空间。查询的时候也是先查询新空间再老空间。

图片来源于王争——数据结构与算法之美

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值