数据结构 - 散列表,再探

news/2025/1/22 20:57:49/文章来源:https://www.cnblogs.com/hugogoos/p/18511696

书接上回,我们继续来聊散列表。

从上面的章节不难发现,无论散列函数怎么构建总会发生碰撞,最多只能降低碰撞概率,但是并不能杜绝碰撞,因此如何解决碰撞问题成了散列重中之重。

01、碰撞解决方案

下面我们一起来看看几种碰撞处理策略。

1、链式法

链式法的核心思想可以理解为把碰撞的key直接放到一个大的集合中;

我们先来回顾一下何为碰撞,碰撞就是两个不同的key,通过散列函数计算出相同的散列值。

为什么发生碰撞就会出问题呢?因为散列表本质上是数组,数组一个下标只能对应一个值,如果key1和key2发生碰撞,意味着key2对应的value2会把key1对应的value1覆盖掉,这就会导致了数据丢失。

想解决这个问题我直观想法是如果发生碰撞时value2不是覆盖value1,而是两者同时存储下来,是不是这个问题就解决了呢?

答案是肯定的,但是里面还有很多细节要处理。如一个数组元素里怎么再存放多个元素呢?对于发生碰撞的key如何查找呢?

首先一个数组元素里怎么存放多个元素?其实方法有很多,比如数组元素中直接存放一个数组,比如数组中存放一个链表表头指向一个链表等等。

在散列表中,每个数组元素所在的位置,我们称为“ 桶” 或者 “ 槽”,而每个桶(槽)又对应一条链表,发生碰撞的所有key都放到相同的桶对应的链表中,这种碰撞处理策略称为链式法。

当存取元素时分两步,第一步先找到key所在的桶,第二步再在桶所对应的链表中存取元素。

这个方案是非常简单实用的,而且因为链表节点是动态申请这也大大提升了空间利用率。

2、开放寻址法

开放寻址法的核心思想可以理解为当发生冲突时,如果当前散列位置已被使用就按某种方式继续探测下一个可以用的散列位置。

而某种方式继续探测具体是指当发生冲突时,则在当前散列值基础上再加上指定的步长并判断当前散列位置是否可用,如果不可用则重复此步骤。

因此开放寻址法可以概况为:hash_value=(hash(key)+step(i))%m,1≤i≤m-1

如上图当如果数组中浅绿代表空地址,浅橙色表示已经被占用,当传入key4时,经过散列函数计算出所在数组索引应该为6,而此时索引6已经被占用,因此继续往后探测,索引8已经被占用,继续往后探测,索引0已经被占用,继续往后探测,索引1空闲,存入value4。

而根据不同的步长生成规则,又可以分成以下几种探测方法:线性探测法、平方探测法、双重散列探测法、随机探测法等等

(1)线性探测法

线性探测法是通过在散列表中线性探测下一个可用位置来解决碰撞。

当发生碰撞时,则按顺序探测下一个元素位置,直至查找到可用的散列地址或者排查完全表。因此当探查到表尾地址时,并不是结束探测而是回到表首继续探测,直至探测到起始探测位置。

其公式可表示为:hash_value=(hash(key)+step)%m,1≤step≤m-1

优点:1、实现简单;2、效率较高,在负载因子较低时特别更为突出。

缺点:可能导致聚集现象,即多个元素在碰撞后会集中在一起,从而导致相邻位置被占用,增加探测时间。

(2)平方探测法

平方探测法是通过使用平方增量来解决哈希冲突,从而有效地分散元素。

如果说线性探测法每次探测的步长是step,那么平方探测法的探测步长则是step的平方。

其公式可表示为:hash_value=(hash(key)+step^2)%m,1≤step≤m-1

与线性探测法相比,平方探测法主要优势在于增量平方数使得探测位置分布更广,位置也就更分散,从而减少元素的聚焦现象,从而提高查找效率。因此平方探测法在处理碰撞时通常优于线性探测法,当然选择哪种方法应根据具体应用场景和性能要求来决定。

优点:减少聚集现象,元素分布均匀。

缺点:1、仍可能存在二次聚集现象(即某些位置可能因为探测规则而频繁被访问)。2、可能无法探测到所有位置,在负载因子较高时尤为突出。

(3)双重散列探测法

双重散列探测法是使用两个散列函数来决定探测序列,从而更有效地分散冲突。也就是当发生碰撞时,会使用第二个散列函数来生成增量从而确定下一个要查找的位置。

其公式可表示为:hash_value=(hash(key)+ i * hash2(key))%m,1≤i≤m-1

优点:1、减少聚集现象,效率高,探测路径较为随机。2、可以探测到所有位置,理论上避免了聚集问题。

缺点:实现相对复杂,且需要选择合适的第二个散列函数。

(4)随机探测法

随机探测法是通过随机选择下一个探测位置来解决冲突。也就是说当发生碰撞时,确定查找的下一个位置的增量式一个随机数。

其公式可表示为:hash_value=(hash(key)+ r)%m,1≤r≤m-1

优点:1、大幅减少聚集现象,位置探测随机性强。2可以有效分散元素,降低碰撞影响。

缺点:1、随机性可能导致性能不稳定,尤其是在负载因子较高时。2、难以预测和调试。

总结

3、再散列法

再散列法的核心思想可用理解为对现有散列表进行扩容并重新计算现有元素位置。

我们先来回忆一个概念负载因子,负载因子式用来衡量散列表填充程度,通过散列表已存储的元素个数除以散列表的大小计算可得。

负载因子过大会导致一些列问题,首先会加大碰撞概率,碰撞概率增大又会导致处理碰撞成本增加,进而导致性能下降。同时也可能导致内存碎片化使用率不高,等等问题。

而负载因子就是触发再散列的时机,当负载因子超过某个阈值(一般是0.75)时,进行再散列。

而再散列的难点在于如何处理新老数据,是当触发再散列时一次性迁移所有老数据到新散列表中,还是分批次迁移老数据直至所有老数据迁移完成为止?

一次性迁移可能实现上相对简单,但是也引发了很多问题,如果迁移过程中又有其他操作怎么办?如果数据量很大迁移时间过长怎么办?

而分批迁移实现上会更复杂一些,要处理好新老数据共存时的查找、插入、删除等操作。

整体思路如下:

(1)当负载因子到达设定的阈值,则重新申请新的内存空间,不进行老数据迁移;

(2)当有新数据要插入时,将新数据插入至新的内存空间中,并取出一个老数据插入到新的内存空间中;

(3)重复步骤(2)直至所有老数据都迁移至新的内存空间为止;

(4)当在新老数据共存进行查找时,可用先在老的空间进行查找,如果不存在再到新空间中查找。

:测试方法代码以及示例源码都已经上传至代码库,有兴趣的可以看看。https://gitee.com/hugogoos/Planner

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/823303.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

学习笔记(十一):ArkUi-弹性布局 (Flex)

弹性布局(Flex)提供更加有效的方式对容器中的子元素进行排列、对齐和分配剩余空间。 常用于页面头部导航栏的均匀分布、页面框架的搭建、多行数据的排列等。 容器默认存在主轴与交叉轴,子元素默认沿主轴排列,子元素在主轴方向的尺寸称为主轴尺寸,在交叉轴方向的尺寸称为交…

《城堡守护者:开启热血守护之旅》

​ 在充满奇幻与冒险的游戏世界中,有一款游戏如同一座闪耀的堡垒,吸引着无数玩家投身其中,它就是《城堡守护者》。这款游戏以其独特的魅力,为玩家们构建了一个充满挑战与激情的世界,让每一位玩家都能化身为英勇的守护者,捍卫属于自己的城堡,书写属于自己的传奇。一、沉…

软件基础第二次作业

.center { width: auto; display: table; margin-left: auto; margin-right: auto }班级链接 https://edu.cnblogs.com/campus/zjlg/rjjc目标 用Text_Counter统计纯文本文件中的字符数、单词数、句子数姓名-学号 侯锦秀-2022329301003码云地址 https://gitee.com/honeyest/text…

植物大战僵尸杂交版游戏

「游戏」链接:https://pan.quark.cn/s/e37b17a6803d

vue2-自定义全局toast提示插件

编写toast.vue 在components文件夹下新增toast文件夹,并在toast文件夹中新增index.vue文件 ` <template><div id="toast"><span class="toast" @mouseleave="start" @mouseenter="stop" :style="{ top: styleTop…

Escalate_Linux靶机提权学习

靶机下载 https://www.vulnhub.com/entry/escalate_linux-1,323/ 用VMware打开 扫描端口 nmap -sS -sV -n -T4 -p- 192.168.93.134 Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-10-04 09:26 CST Nmap scan report for 192.168.93.134 Host is up (0.00090s latency). …

使用Ubuntu系统管理包工具(apt)部署Zabbix企业级监控系统

1. 系统版本Ubuntu VERSION=22.04.4 LTS2. zabbix安装 2.1 zabbix官方 https://www.zabbix.com/cn/download?zabbix=7.0&os_distribution=ubuntu&os_version=22.04&components=server_frontend_agent&db=mysql&ws=apache 2.2 配置选择2.3 使用Ubuntu的管…

使用node的npm安装包遇到的问题

1最开始安装出现的问题是执行后卡顿出来以下网络相关的错误2搜索后执行命令切换到淘宝镜像后在执行,但是第一我仔细看报错信息,就又切换官网镜像了,又报了1的错误,然后又切回淘宝镜像分析错误信息。发现是安装node的目录用户没有写的权限 3.修改node安装目录文件权限后,在设…

3D在UI上的应用

一、传统管理系统登录页的局限性 在过去,传统的管理系统登录页通常采用平面设计,以简洁的布局和清晰的文字为主。虽然这种设计能够满足基本的功能需求,但也存在一些局限性。首先,平面设计缺乏立体感和深度感,容易给人单调、乏味的感觉。用户在面对这样的登录页时,往往缺乏…

【BUUCTF】Youngter-drive

Youngter-drive UPX脱壳 分析这是一道多线程题,先来分析这个main函数::hObject = CreateMutexW(0, 0, 0); : 创建一个互斥锁,使两个双线程能够使用共享资源hObject = CreateThread(0, 0, StartAddress, 0, 0, 0); Thread = CreateThread(0, 0, sub_41119F, 0, 0, 0); :创…

34. 过滤条件、多表查询、子查询

1. 过滤条件 1.1 过滤条件之having [1]概念 HAVING 子句用于对分组后的结果进行过滤。它通常与 GROUP BY 子句一起使用,在 SELECT 语句的聚合函数(如 SUM(), AVG(), COUNT(), MAX(), MIN() 等)之后应用条件。 HAVING 子句与 WHERE 子句类似,但 HAVING 适用于分组后的数据,…

Debian12 搭建LNMP环境,配置SSL证书,安装WordPress

一、安装并配置PHP SSH连接上VPS之后,我们先更新一下系统组件,使用下面的命令。 apt update -y && apt upgrade -y接着输入下面的命令安装PHP和相关组件 apt install php-fpm php-mysql php-gd php-cli php-curl php-mbstring php-zip php-opcache php-xml php-mysql…