散列表

定义

  • 散列表:数组+链表
  • 一种k-v数据结构,用的是数组支持的按照下标随机访问数据,时间复杂度为O(1)的特性,算是数组的一种扩展,由数组演化而来。可以说没有数组,就没有散列表。
  • key对应的键值,通过散列函数,转化为一个非负整数,这样直接通过直接访问此整数的下标地址(时间复杂度为O(1))的数组,进行增删改查等操作。
  • 常用解决hash冲突方式是:拉链存储

组成

key/关键字

散列函数

散列值

散列函数

基本要求

  1. 散列函数计算得到的散列值必须是一个非负整数(因为数组下标从0开始)。
  2. 当key1==key2时,那么hash(key1)==hash(key2)。
  3. 当key1!=key2时,那么hash(key1)!=hash(key2),但在真实的开发场景中,散列冲突无法避免,即使业界比较著名的MD5,SHA等也会产生散列冲突。

解决散列冲突

开发寻址法

开放寻址法的核心思想是,在出现散列冲突的时候,我们就重新探测一个空闲位置,将其插入。

探测方法

线性探测

插入数据,当根据hash(key)的存储位置被占用,就依次向后探测,找到末尾如果没有,就从头开始探测,直到找到一个空闲位置,将其值插入。

读取数据,当根据hash(key)读取到存储位置元素时候,跟原值比较,当不等于原值时,就依次向后查找,都不是就从头查找,最后找到了即返回,如果找到空闲位置时候,还没有找到,就说明此元素没有存储在此hash表内,返回空即可。

线性探测缺点

随着插入数据越来越多,探测的次数越来越大,在插入时候,也许要探测整张hash表,才能有结果,这样就退化成O(n)的时间复杂度。读取和删除,同样要探测多次,才有结果。

二次探测

线性探测,步长为1,探测的下标序列为hash(key)+0,hash(key)+1,hash(key)+2,....,二次探测步长变为原来的”二次方“,探测的下标序列为hash(key)+0^2,hash(key)+1^2,hash(key)+2^2,.....。

双重散列

使用一组散列函数,hash1(key),hash2(key),hash3(key),...,当出现冲突时候,顺序向后更换散列函数。

链表法

也叫”拉链法“或”拉链存储“是更加常用的解决散列冲突的方法。就是对应每个槽位后,存储一个链表,插入数据时候,直接hash(key)找到对应槽位,插入到链表末尾即可,时间复杂度为O(1)。当查找和删除时候,也是直接定位到槽位拉取链表,遍历链表跟原始数据对比,即可得出结果。时间复杂度为链表长度k成正比即为O(k),针对散列比较均匀的散列函数来说,k=n/m(m为槽位)。

扩容

一次性扩容

  1. 在添加新数据时,触发装载因子(有数据的槽位/hash算法槽位总量)达到一个阀值时,需要扩容,就先从新申请一个一般2倍大小的数组,然后用新的hash算法一次性将原来老hashTable的值都迁移到新表中,然后继续添加新元素,以后的所有操作都在新的hashTable中添加。 缺点:当老hashTable的数据量比较大的时候,一次性迁移比较耗时,这样容易导致上位系统请求超时,这样不可容忍。
  2. 动态扩容:装载因子达到扩容阀值时,先申请一般2倍大小的新数组,先不迁移,新老hashTable并存,每次有操作时候,对应操作完时,动态迁移一个老的值,如此循环,迁移所有的老值。注意:新老hashTable并存时候,在读取时候,新老都得查一下,都没有才算没有值。

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值