数据结构与算法-基础算法篇-散列表(Hash Table)

1. 散列表

1. 什么是散列表

  1. 散列表来源于数组,它借助散列函数对数组这种数据结构进行扩展,利用的是数组支持按照下标随机访问元素的特性。
  2. 需要存储在散列表中的数据我们称为键,将键转化为数组下标的方法称为散列函数,散列函数的计算结果称为散列值。
  3. 将数据存储在散列值对应的数组下标位置。
  4. 散列表两个核心问题是散列函数设计和散列冲突解决。散列冲突有两种常用的解决方法,开放寻址法和链表法。散列函数设计的好坏决定了散列冲突的概率,也就决定散列表的性能。

2. 如何设计散列函数?

总结3点设计散列函数的基本要求:

  1. 散列函数计算得到的散列值是一个非负整数。
  2. key1=key2,则hash(key1)=hash(key2)
  3. key≠key2,则hash(key1)≠hash(key2)

正是由于第3点要求,所以产生了几乎无法避免的散列冲突问题。

3. 散列冲突的解放方法?

  1. 常用的散列冲突解决方法有2类:开放寻址法(open addressing)和链表法(chaining)
  2. 开放寻址法
    1. 核心思想:如果出现散列冲突,就重新探测一个空闲位置,将其插入。
    2. 线性探测法(Linear Probing):
      1. 插入数据:当我们往散列表中插入数据时,如果某个数据经过散列函数之后,存储的位置已经被占用了,我们就从当前位置开始,依次往后查找,看是否有空闲位置,直到找到为止。
      2. 查找数据:我们通过散列函数求出要查找元素的键值对应的散列值,然后比较数组中下标为散列值的元素和要查找的元素是否相等,若相等,则说明就是我们要查找的元素;否则,就顺序往后依次查找。如果遍历到数组的空闲位置还未找到,就说明要查找的元素并没有在散列表中。
      3. 删除数据:为了不让查找算法失效,可以将删除的元素特殊标记为deleted,当线性探测查找的时候,遇到标记为deleted的空间,并不是停下来,而是继续往下探测。
        结论:最坏时间复杂度为O(n)
    3. 二次探测(Quadratic probing):线性探测每次探测的步长为1,即在数组中一个一个探测,而二次探测的步长变为原来的平方。
    4. 双重散列(Double hashing):使用一组散列函数,直到找到空闲位置为止。
    5. 线性探测法的性能描述:
      用“装载因子”来表示空位多少,公式:散列表装载因子=填入表中的个数/散列表的长度。
      装载因子越大,说明空闲位置越少,冲突越多,散列表的性能会下降。
  3. 链表法(更常用)
    1. 插入数据:当插入的时候,我们需要通过散列函数计算出对应的散列槽位,将其插入到对应的链表中即可,所以插入的时间复杂度为O(1)
    2. 查找或删除数据:当查找、删除一个元素时,通过散列函数计算对应的槽,然后遍历链表查找或删除。对于散列比较均匀的散列函数,链表的节点个数k=n/m,其中 n 表示散列表中数据的个数,m 表示散列表中槽的个数,所以是时间复杂度为O(k)

4. 思考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值