当前位置: 首页 > news >正文

如何解决哈希冲突?

目录

1. 链接法

2. 开放寻址法

2.1. 线性探测

2.2. 二次探测

2.3. 双重哈希

3. 再哈希法

4. 哈希桶扩容

5.方法比较 

5.1. 链接法

5.2. 开放寻址法

5.3. 再哈希法

5.4. 哈希桶扩容

哈希表就是通过散列函数将键映射到定值,简单来说就是一个键对应一个值。

而通过散列函数映射时将两个键映射到了同一个值,即这两个键将被哈希表映射到同一个位置,这种情况就被称为哈希冲突。

解决哈希冲突通过有四种方法:

  • 链接法
  • 开放寻址法
  • 再哈希法
  • 哈希桶扩容

1. 链接法

每个哈希表的槽位维护一个链表或其他数据结构,当多个元素被哈希到同一个槽位时,它们会被放在这个槽位的链表中。查找时会遍历链表,插入时也会直接加到链表中。

假设我们有一个哈希表,哈希函数将键值映射到以下槽位:

  • 0: [5, 15]
  • 1: []
  • 2: [2]
  • 3: []
  • 4: [4]

当我们插入键值 5 和 15 时,它们都被映射到槽位 0。因此,它们会形成一个链表:

槽位 0: [5 -> 15]
槽位 1: []
槽位 2: [2]
槽位 3: []
槽位 4: [4]

2. 开放寻址法

当发生冲突时,算法会寻找下一个可用的槽位。常见的探查方式有线性探查、二次探查和双重哈希等。这种方法不使用额外的存储结构,而是在哈希表内部处理所有元素。开放寻址法的几种常见探测方法确实包括线性探测、二次探测和双重散列。以下是每种方法的详细说明和示例:

2.1. 线性探测

在发生冲突时,线性探测会逐个检查后续的槽位,直到找到一个空槽。例如:

假设哈希函数为 h(k) = k % 5

  • 插入 1 → 槽位 1(成功)
  • 插入 6 → 槽位 1(冲突),检查 2(成功)
  • 插入 11 → 槽位 1(冲突),2(冲突),3(成功)

最终哈希表:

槽位 0: []
槽位 1: [1]
槽位 2: [6]
槽位 3: [11]
槽位 4: []

2.2. 二次探测

二次探测在发生冲突时采用平方递增的方式查找空槽。例如:

哈希函数仍然假设是 h(k) = k % 5

  • 插入 1 → 槽位 1(成功)
  • 插入 6 → 槽位 1(冲突),检查 1^2 → 2(成功)
  • 插入 11 → 槽位 1(冲突),检查 1^2(冲突),2^2 → 4(成功)

最终哈希表:

槽位 0: []
槽位 1: [1]
槽位 2: []
槽位 3: []
槽位 4: [6]

2.3. 双重哈希

双重散列使用第二个哈希函数来决定步长,以解决冲突。例如:

假设第一个哈希函数 h1(k) = k % 5,第二个哈希函数 h2(k) = 1 + (k % 4)

  • 插入 1 → 槽位 1(成功)
  • 插入 6 → 槽位 1(冲突),步长 h2(6) = 1 + (6 % 4) = 3,检查 1 + 3 = 4(成功)
  • 插入 11 → 槽位 1(冲突),步长 h2(11) = 1 + (11 % 4) = 3,检查 1 + 3 = 4(冲突),再检查 4 + 3 = 2(成功)

最终哈希表:

槽位 0: []
槽位 1: [1]
槽位 2: [11]
槽位 3: []
槽位 4: [6]

3. 再哈希法

在发生冲突后,可以使用另一个哈希函数对该元素进行再哈希,找到一个新的槽位。

使用初始哈希函数 h1(k) = k % 5,当插入 10 时:

  • h1(10) = 0(槽位 0 已占用)
  • 使用新的哈希函数 h2(k) = (k / 5) % 5,计算:
    • h2(10) = 2(槽位 2 已占用)
  • 再次使用 h1 计算:
    • h1(10 + 1) = 1(槽位 1 已占用)
    • h1(10 + 2) = 3(放入槽位 3

最终哈希表如下:

槽位 0: [0]
槽位 1: [1]
槽位 2: [2]
槽位 3: [10]
槽位 4: [4]

4. 哈希桶扩容

如果哈希表的负载因子超过某个阈值,可以增加哈希表的大小,并重新计算所有元素的哈希值并重新分配到新的槽位。这有助于减少冲突并提高性能。

哈希表的负载因子是一个衡量哈希表填充程度的重要指标,通常用公式表示为:

负载因子 = 哈希表中的元素数量 / 哈希表的槽位总数

负载因子的意义:

  • 高负载因子:当负载因子接近或超过 1 时,表示哈希表的槽位几乎被填满,可能导致更多的哈希冲突,从而影响查找、插入和删除的性能。
  • 低负载因子:负载因子较低时,哈希表的空槽较多,冲突较少,性能较好,但会导致内存浪费。

负载因子的调整:

通常,当负载因子超过某个设定的阈值(例如 0.7 或 0.75),就会进行扩容。扩容时,哈希表的槽位数量增加,所有元素需要重新哈希并放入新的槽位中。

假设哈希表的大小为 5,当前负载因子超过 0.7,我们决定扩容到 10。在扩容时,所有元素的哈希值需要重新计算:

  • 原哈希表:
槽位 0: [0]
槽位 1: [1]
槽位 2: [2]
槽位 3: [3]
槽位 4: [4]
  • 扩容后,哈希函数改为 h(k) = k % 10,插入后的哈希表:
槽位 0: []
槽位 1: [1]
槽位 2: [2]
槽位 3: [3]
槽位 4: [4]
槽位 5: [5]
槽位 6: []
槽位 7: []
槽位 8: []
槽位 9: []

5.方法比较 

5.1. 链接法

优点:

  • 容易实现,简单明了。
  • 动态性好,可以存储任意数量的元素,只受限于内存。
  • 插入和删除操作较快,不需要重新哈希。

缺点:

  • 在某些情况下,链表可能会很长,导致查找性能下降。如果链表过长,可能导致性能接近于线性查找。
  • 需要额外的内存来存储链表节点。

5.2. 开放寻址法

优点:

  • 所有元素都存储在哈希表内部,节省了额外的内存。
  • 不需要额外的链表,查找时不需要遍历。

缺点:

  • 哈希表的负载因子需要控制在较低水平,通常小于 0.7,否则性能显著下降。
  • 在频繁冲突的情况下,查找效率会下降,且可能需要进行多次探测。
  • 删除操作复杂,可能导致“探测链”的问题,影响后续查找性能。

5.3. 再哈希法

优点:

  • 通过使用不同的哈希函数,能有效地减少冲突。
  • 可与其他方法结合使用,灵活性高。

缺点:

  • 需要额外的计算和存储开销,可能导致性能下降。
  • 在大量元素插入时,可能需要频繁地进行哈希计算。

5.4. 哈希桶扩容

优点:

  • 通过扩容可以有效降低负载因子,从而减少冲突。
  • 能够保持较高的性能,特别是在处理大量数据时。

缺点:

  • 扩容过程可能需要遍历整个哈希表,重新计算哈希值,导致短时间内性能下降。
  • 增加了内存的使用和管理复杂度。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 828华为云征文 | 云服务器Flexus X实例:RAG 开源项目 FastGPT 部署,玩转大模型
  • 算法揭秘:时间复杂度与空间复杂度的实用指南
  • Docker:解决开发运维问题的开源容器化平台
  • 使用python写按键程序
  • 产品经理面试整理-准备个人案例
  • MySQL关卡任务书
  • 在 Flutter 开发中如何选择状态管理:Provider 和 GetX 比较
  • notepad++的json查看
  • 【通俗易懂】知识图谱增强 RAG 思路 和 实现方案
  • HTTP中的301、302实现重定向
  • css禁止图片保存,CSS中的图片保存方法
  • 9月22日正式签约,树莓集团落子海南!
  • Spring MVC 全局异常 总结
  • 力扣题解1014
  • C语言从头学62——学习头文件stdlib.h(一)
  • Google 是如何开发 Web 框架的
  • 分享一款快速APP功能测试工具
  • [ JavaScript ] 数据结构与算法 —— 链表
  • 2017 前端面试准备 - 收藏集 - 掘金
  • Angular 响应式表单之下拉框
  • CentOS从零开始部署Nodejs项目
  • CSS3 聊天气泡框以及 inherit、currentColor 关键字
  • IIS 10 PHP CGI 设置 PHP_INI_SCAN_DIR
  • Java Agent 学习笔记
  • LeetCode29.两数相除 JavaScript
  • PAT A1120
  • Python进阶细节
  • springboot_database项目介绍
  • uni-app项目数字滚动
  • vue 配置sass、scss全局变量
  • 从0到1:PostCSS 插件开发最佳实践
  • 从零开始的webpack生活-0x009:FilesLoader装载文件
  • 搞机器学习要哪些技能
  • 个人博客开发系列:评论功能之GitHub账号OAuth授权
  • 技术:超级实用的电脑小技巧
  • 力扣(LeetCode)22
  • 爬虫模拟登陆 SegmentFault
  • 配置 PM2 实现代码自动发布
  • 普通函数和构造函数的区别
  • 前端每日实战:70# 视频演示如何用纯 CSS 创作一只徘徊的果冻怪兽
  • 使用 Node.js 的 nodemailer 模块发送邮件(支持 QQ、163 等、支持附件)
  • 世界编程语言排行榜2008年06月(ActionScript 挺进20强)
  • 我有几个粽子,和一个故事
  • CMake 入门1/5:基于阿里云 ECS搭建体验环境
  • ionic入门之数据绑定显示-1
  • 我们雇佣了一只大猴子...
  • ​什么是bug?bug的源头在哪里?
  • #android不同版本废弃api,新api。
  • #mysql 8.0 踩坑日记
  • #传输# #传输数据判断#
  • (007)XHTML文档之标题——h1~h6
  • (13):Silverlight 2 数据与通信之WebRequest
  • (2)leetcode 234.回文链表 141.环形链表
  • (20)目标检测算法之YOLOv5计算预选框、详解anchor计算
  • (编程语言界的丐帮 C#).NET MD5 HASH 哈希 加密 与JAVA 互通