分享好友 最新动态首页 最新动态分类 切换频道
数据结构——查找篇(构造哈希函数;开发地址法、分离链接法)
2024-12-25 14:08

数据结构——查找篇(构造哈希函数;开发地址法、分离链接法)

描述

散列法又称为关键字—地址转化法
散列查找实际为一种以空间换时间的查找方式,当散列表中元素过多,装填因子过大的时候,就会导致查找效率大幅度下降;当散列表中元素较少的时候,查找效率则可以达到常数级别

概念

散列函数/哈希函数
一个把关键字映射为该关键字对应的散列地址的函数,记为Hash(key)= Addr
这里的地址可以为数组下标、索引、或者内存地址等
构造函数的注意点
1.定义域必须包含所有的关键词,值域取决于散列表的表长
2.不同的关键词计算出来的地址应该要等概率,较为均匀地分布在整个散列表中,以减少冲突的发生
3.函数应该尽可能简单,计算时间要短

散列表/哈希表
根据关键字直接进行数据访问的数据结构,散列表建立了关键字和存储地址之间的一种映射关系(这种映射关系就是散列函数所描述的

关键词key
即为我们使用散列法存储在散列表中的目标数据
key可能并不是真正的用户需要查找的数据内容,而是一个守门人,找到了key,也就相当于找到了用户真正需要的数据,所以如何找到key才是关键(类比于我们使用搜索引擎搜索相关关键字,从而得到我们实际需要的数据的过程

冲突
如果多个key值代入散列函数/处理冲突的算法中,计算结果(即散列地址)相同,则称发生了冲突
通常key的值域远远大于表空间的地址集,所以冲突不可避免,但是要通过设计合理的散列函数把冲突出现的概率尽可能减低,或者通过一些方法来对已经发生的冲突状况进行合理解决
从数学的角度看,所谓冲突次数其实就是散列函数的“多对一”的映射的程度,最理想是全部为“一对一”,即完全不会发生冲突现象,但是这个是基本不可能实现的

同义词
两个key,代入散列函数中,得到的散列地址完全相同(发生了冲突,则这两个key互为对方的同义词

装载因子/装入因子
如果针对数组的存储形式,即为已经填入表中元素的个数除以散列表空间的大小
如果针对链表的存储形式(即使用了分离链接法,即为每一个链表的平均长度

散列表溢出
如果使用了冲突的解决方法,依旧无法插入新元素,则称出现了散列表溢出的情况

懒惰删除
散列表不会进行数据的完全删除,即一个key填入散列表的一个单元中,如果后续将这个key删除,则这个单元在删除key后所处状态和一个从来就没有元素填入的单元所处的状态,是不一样的,代码中通过单元的Info成员变量进行单元状态的区分

分离链
为处理冲突的方法中的分离链接法中提及的概念,在同一个分离链中,各个结点的key都互为同义词,每一个分离链都是具有头结点的单链表,分离链中,结点的数据区域存放key,指针区域存放下一个结点的地址

平均查找次数
“查找次数"指的是进行key值比较的次数,“平均"指的是每一项的查找次数之和,再除以项数
需要注意的是,无论是计算哪一种次数,都是要把确认"失败"或者"成功"的那一次计算在内,即计算公式中的那个”+1”

构造散列函数的方法
1.数字关键字的散列函数构造

  • 直接定址法
    - 散列函数为a x key + b(一个关于key的线性函数

  • 除留余数法
    - 散列函数为key%p
    - 散列表长一般取关键词集合大小 / 允许的最大装填因子
    - 一般p选取一个小于等于散列表长的素数,因为这样求得的散列地址比较均匀地分布在散列表中 的可能性会比较大,减少冲突发生

  • 数字分析法
    - 散列函数为把数字关键字中的某些随机性高的位上的数值提取出来,组合为一个数值(还可以再进行除留余数法,得到散列地址
    - 先对数字关键字的每一位的随机性进行评估,选取随机性高的几个位上的值,以此避免聚集效应

2.字符串关键字的散列函数构造

  • ASCII加和法
    - 散列函数为把每一个字符对应的ASCII码相加,得到的结果再对散列表长进行取余

  • 前三个字符移位法
    - 散列函数为先把key中的前三个字符的ASCII码得到,然后把这三个整数分别当作27进制的各个位上的数值,不同的位乘以不同的权重,转化为一个十进制数值,再对散列表长进行取余

  • 移位法(为前三个字符移位法的升级版,全部字符的ASCII码都参与计算
    - 散列函数为先把key中的全部字符的ASCII码得到,然后把所有的整数都分别当作32进制的各个位上的数值,不同的位乘以不同的权重,转化为一个十进制数值,再对散列表长进行取余
    - 选取32进制的原因为,32=2^5,乘以32相当于对该十进制数值的二进制形式按位左移5位的得到的结果数值,方便C语言的代码实现

解决冲突的方法
1.开放地址法(开放地址法中的地址尝试都是循环式的

  • 线性探测法
    - 每一次遇到冲突,都是尝试相邻的下一个地址
    - 增量序列为1,2,3,…,散列表长-1
    - 这种方式明显可以探查到整个散列表空间
    - 可能会导致“一次聚集”

  • 平方探测法
    - 遇到冲突,交叉尝试左右两边的地址
    - 增量序列为12,-12,22,-22,32,…,q2,-q2(q<=散列表长开根号后向下取整的结果数值
    - 如果散列表长为某一个4*k+3的整数,则平方探测法可以探查到整个散列表空间
    - 可能会导致“二次聚集”

  • 双散列探测法
    - 遇到冲突,尝试的散列地址由另一个散列函数h2(key)计算得到
    - 增量序列为h2(key,2*h2(key,3*h2(key)…
    - h2(key)的值必须要保证不为0,否则会导致某一些key值一直无法插入散列表中
    - 一个例子:h2(key= p -(key % p,p为小于散列表表长的素数,也必须使用一个素数作为散列表表长,否则可能会导致探查不到整个散列表空间
    - 使用双散列探测法会增加每次探测的计算量,但是相对于平方探测法,可以减少探测次数

  • 再散列探测法
    - 装填因子过大的时候,就进行散列表的加倍扩大,提高查找速度,但是将原表中的数据重新分配到新表中,这个搬运的过程会花费较多的时间(在交互系统中会让人感觉有"停顿",在一些对时延要求较低的系统中必须要谨慎使用该方法

2.分离链接法
几个特点
1.链式存储
不再使用顺序存储的方式进行key的存储,转而使用链式存储,同义词都以链表结点的形式存放在同一个分离链(一个具有头节点的单链表)中,以此来解决冲突问题

2.分离链的头节点
在线性表那篇中,我们发现如果带一个头节点,那么插入和删除的代码实现都会简洁很多,于是我们选择每一个分离链都带有一个头节点,而且把头节点全部放入一个结构体数组,进行统一管理

3.新key的存储
为了写代码的方便,也考虑到新的key被访问到的概率相对于之前存放的key较大,于是我们决定插入携带新的key的结点的时候,一律在分离链的头节点的后一个位置进行插入,这样可以提高访问效率

小贴士

散列法的两个基本内容
1.构造散列函数的方法
2.解决冲突的方法

散列表的运作过程解析
1.关键字key的存储
通过构造一个散列函数,把要存储的key代入,计算出一个数值,这个数值可能就是目标存储单元的地址(“可能”表示如果发生了冲突,要使用处理冲突的算法不断尝试,换一个新地址进行数据存储

2.关键字key的查找
依据给定的key,代入散列函数中,得到一个数值,这个数值可能就是目标存储单元的地址,在计算出地址以后,还要进行对应单元状态的查看和key的比对,查看是否为所要寻找的key
在查找过程中,会出现这些情况
(1)如果单元状态为Empty(说明目标key未存入)或者存储的key和所要查询的key一致(说明目标key在散列表中存在,或者已经被删除了,返回这个地址,结束查找
(2)如果存储的数据内容和所要查询的数据内容不一致,说明当时存储这个目标key的时候,可能使用了处理冲突的算法把这个key存入了一个新地址中,所以此时也同样要运行相同的算法,继续尝试可能的地址,继续进行key的比较,继续查找

从上面的讲解中,可以发现,其实在完成数据查找的时候,也就完成了要插入的位置的查找(可以类比于二叉搜索树的插入算法,有异曲同工之妙

为什么需要进行懒惰删除
首先明确一点,在查找key的时候必须使用和key插入时相同的计算方法去找到目标地址,才能把key找到。具体来说,如果在key插入时,使用了处理冲突的算法,根据处理冲突的算法的原理:发生冲突了,就会继续运行算法中的代码,直到找到一个单元状态为Empty的单元或者找到一个单元状态不是Empty,而存储的key就是要查找的key的单元才会结束查找,返回该单元地址
假设进行的不是懒惰删除(即进行key删除的时候,直接把单元状态设置为Empty,会导致我们在查找的时候,如果像存放目标key的时候一样,使用相同的处理冲突的算法去寻找目标key,在运行算法的过程中,尝试到了某一个地址,而这个单元在我们插入要查找的目标key之后,进行了该单元key的直接删除,即直接把单元状态设置为Empty,按照查找算法的原理,此时找到一个单元状态为Empty的单元,会直接退出循环,进行返回这个单元状态为Empty的单元,表示目标key在散列表中不存在,但是我们知道,实际上目标key的的确确是存在于这个散列表当中的"进行了key的删除操作,直接把单元状态设置为Empty"这样的操作就造成了要查找的目标key在散列表中不存在的假象,这种现象我们称为发生了断链

平均查找次数的具体计算方法
值得注意的一点是,如果发现地址对应的key并不是目标key,此时会启动处理冲突的算法,而不是结束函数,这是和以前的查找所不同的地方

1.开放地址表示法的成功查找的平均查找次数(即每一个元素的成功查找次数相加/元素个数
因为计算的是“成功查找”,于是项数为哈希表中已存储的元素的个数(因为只有确实存储了这个key,才能够实现成功查找,对于每个元素的成功查找次数的计算,要考虑是否出现了冲突的情况,要考虑使用的是哪个处理冲突的方法,每一个元素的成功查找次数为 查找过程的冲突数+1(这个1是最后成功找到目标key的那一次查找

2.开放地址表示法的失败查找的平均查找次数(即哈希表中所有可以被哈希函数计算出来对应地址的格子对应的失败查找次数相加/格子个数
因为计算的是“失败查找”,而失败的原因在于查找的key不在哈希表中,项数为哈希表中所有可以被哈希函数计算出来对应地址的格子的个数,要考虑是否出现了冲突的情况,要考虑使用的是哪个处理冲突的方法,每一个格子对应的失败查找次数为 查找过程的冲突数+1(这个1是最后确认散列表中不存在目标key的那一次查找,即查找到状态为Empty的单元

3.分离链接法的成功查找的平均查找次数(即每一个元素的成功查找次数相加/元素个数
项数为哈希表中已存储的元素的个数,由于每一个分离链都是一个单向链表,只能从头节点开始向后进行查找,于是每一个元素的成功查找次数即为对应的结点在其分离链中的次序(头结点算第0个结点

4.分离链接法的失败查找的平均查找次数(即把每一个分离链的长度相加,结果除以分离链的个数
和开放地址表示法的失败查找的平均查找次数的计算差不多,上面的项数为哈希表中所有可以被哈希函数计算出来对应地址的格子数,这里的项数其实就是分离链的个数,每一项的失败查找的次数,这里就是每一个分离链的长度,于是计算公式为:把每一个分离链的长度相加,结果除以分离链的个数

关键参数的宏定义及类型重命名

 
 

结构定义

 
 

(1)返回第一个大于N的素数作为散列表的实际表长 的函数

 

(2)散列函数

 

(3)初始化

 
 

(4)查找key

 

(5)插入key

 
 

(4)查找key

 

(5)插入key

 

(6)打印散列表

 

主函数测试

 
 
 

关键参数的宏定义及类型重命名

 

结构定义

 
 

(1)散列函数

 

(2)初始化

 

(3)查找key

 

(4)插入key

 

(5)打印散列表

 

主函数测试


最新文章
暴躁老阿姨玩CSGO-友:这才是真正的战斗精神!
在一个阳光明媚的下午,暴躁老阿姨坐在电脑前,准备开始她的CSGO游戏。虽然她的手指因岁月的洗礼而有些僵硬,但她对游戏的热爱却让她燃起了无限激情。她的激烈操作让旁观的网友惊叹不已,纷纷在评论区留言:“这位阿姨简直是我们心目中的战
工商银行吕仲涛:金融行业AI大模型工作实践
  近日,由用友主办的2024全球商业创新大会在北京隆重召开。中国工商银行(以下简称“工商银行”)首席技术官吕仲涛出席企业数智化价值峰会,并发表题为《金融行业AI大模型工作实践》的精彩演讲(文末查看演讲视频)。吕仲涛表示,工商银行体
郑州优质公立中专学校排名 2024年招生信息
随着职业教育的快速发展,越来越多的学生和家长开始关注并选择优质的中等专业学校。郑州作为河南省的省会城市,拥有众多优质的公立中专学校,为学生提供了多样化的学习和职业发展路径。以下是2024年郑州优质公立中专学校的排名及招生信息,
百度竞价推广代运营公司
百度竞价推广代运营公司,专注于为企业提供全方位的竞价推广服务。通过精准的关键词策略、创意广告设计和持续优化,帮助企业提升在百度搜索结果中的曝光率,吸引更多潜在客户。代运营公司还提供专业的数据分析,根据数据调整推广策略,确保
最新跨境电商复习课程
Wish一、单选题(2 分/题,共40 分)1.Wish 平台有大约(D )的订单来自于移动端。A. 80%B. 85%C.90%D. 95%2.Wish 的注册用户大部分年龄层在(B )。A. 20sB. 30sC. 40sD. 50s3.Wish 是一家来自(B )的创业企业。A. 印度B. 美国C. 德国D.
【R7s(移动4G)应用宝下载】OPPO R7s 移动4G应用宝8.8.6免费下载
(Android)是腾讯应用中心倾力打造的手机应用商店,致力于为用户丰富、优质、个性化的安卓软件资源和一站式的下载管理体验,全方位覆盖用户的下载、管理、收藏、分享、等多样化需求应用宝2024更新内容1、修复了一些已知的bug应用宝6.7更新
声波清理大师sonic官方版app2024免费下载安装最新版
Sonic声波助手,一般又称声波清理大师sonic。Sonic声波助手是以坤功能十分强大专业声波助手软件,能够有效的释放低频音频,能够清理手机喇叭中的灰尘污渍,当手机进水的时候还能有效排出手机进的水,非常的方便,还能播放自然声音频,聆听
第一批AI原住民开始变现:9岁小学生,用大模型写书赚2万
撰文 | 江小玉 编辑 | 龚 正 当人们正在观望,AI什么时候抢走自己的饭碗时,北京一名9岁的小学生在AI的帮助下写了一本小说,并赚到了2万元的版税。 这件看似不可思议的事,他是如何做到的?此外,他还
Nginx与Web安全:遵循OWASP最佳实践
在当今数字化时代,网络安全已成为企业不可忽视的重要环节。Web应用程序面临着各种威胁,包括SQL注入、跨站脚本攻击(XSS)、跨站请求伪造(CSRF)等。Nginx作为高性能的HTTP和反向代理服务器,
相关文章
推荐文章
发表评论
0评