解决哈希冲突的办法

链地址法(Chaining)和开放地址法(Open Addressing)是哈希表中解决哈希冲突(不同键映射到相同位置)的两种主要策略。

一、链地址法

哈希表的每个槽位(桶)不直接存储键值对本身,而是存储一个链表(或其他容器,如红黑树)的指针。当发生哈希冲突时(即多个键被哈希到同一个槽位),这些键值对会被添加到该槽位对应的链表中。
原理

  • 插入: 计算键的哈希值,找到对应的槽位。将新的键值对插入到该槽位所指向的链表中(通常插入链表头部或尾部)。
  • 查找: 计算键的哈希值,找到对应的槽位。遍历该槽位指向的链表,查找目标键。
  • 删除: 计算键的哈希值,找到对应的槽位。遍历该槽位指向的链表,找到目标键值对并将其从链表中移除。

二、开放寻址法

1.核心思想

  • 哈希表的每个槽位直接存储键值对(或者一个标记)。
  • 当发生哈希冲突时,不会使用额外的数据结构(如链表),而是根据一个预先定义的探测序列(Probing Sequence)在哈希表中寻找下一个空闲的槽位来存放冲突的键值对。
  • 所有元素都存储在数组本身中。

2.原理:

  • 插入:
    • 计算键的初始哈希值 h0 = hash(key)。
    • 如果槽位 h0 空闲,则直接插入。
    • 如果槽位 h0 已被占用(发生冲突),则根据探测序列计算下一个位置 h1, h2, h3, …,直到找到一个空闲槽位插入。如果遍历完整个表都没找到空闲槽位,则哈希表已满,需要扩容。
  • 查找:
    • 计算键的初始哈希值 h0 = hash(key)。
    • 检查槽位 h0:
      • 如果槽位为空,说明键不存在。
      • 如果槽位不为空且键匹配,则查找成功。
      • 如果槽位不为空但键不匹配(冲突),则根据探测序列计算下一个位置 h1, h2, h3, …,继续检查。
      • 在探测过程中,如果遇到一个真正空闲的槽位(不是被删除标记占据),说明键不存在。
  • 删除:
    • 不能简单地将找到的槽位置为空。因为这可能会中断后续的查找探测序列(后续查找遇到空槽会误认为键不存在)。
    • 常见的做法是使用一个特殊的标记(如 DELETED)来标记被删除的槽位。
    • 插入操作可以将 DELETED 标记的槽位视为空闲槽位并插入新元素。
    • 查找操作遇到 DELETED 标记时需要继续探测。
    • 这种标记可能导致“脏”槽位积累,需要定期清理(如rehash)。

3.探测序列类型

  • 线性探测: hi = (h0(key) + i) % TableSize (i = 1, 2, 3, …)。最简单,但容易产生聚集(Clustering),导致性能下降。
  • 二次探测法: 当发生冲突时,根据一个二次函数来确定下一个探测位置。
    • hi = (h0(key) + Ci) % TableSize (Ci是一个二次函数,去+/- i的平方)。
    • 与线性探测相比,二次探测法可以避免“线性聚集”问题,即冲突的元素不会集中在连续的位置上。但是可能会出现“二次聚集”,即某些位置被频繁探测。
  • 随机探测: 当发生冲突时,随机选择一个位置作为下一个探测点。
    • 具体来说,假设初始哈希地址为 h(key),则探测序列可以表示为:hi = (h0(key) + i) % TableSize (i是一个随机数)。
  • 再哈希法: 通过使用两个不同的哈希函数来确定探测序列,从而避免冲突聚集问题,提高哈希表的性能。
    • 当发生冲突时,使用第二个哈希函数来计算偏移量,而不是固定地增加一个常量或二次函数值。
    • hi = (h1(key) + i*h2(key) % TableSize。h1(key)是主哈希函数,用于计算初始地址。h2(key)是次哈希函数,用于计算偏移量。i 是探测次数,从 1 开始递增。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值