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

数据结构基础--散列表

数据结构基础——散列表(17)

散列表的定义

散列表(哈希表,Hash Table):是一种数据结构。特点是:可以根据数据元素的关键字计算出它在散列表中的存储地址

散列函数(哈希函数):Addr=H(key)\text{Addr=H(key)}Addr=H(key)建立了 “关键字”→“存储地址” 的映射关系。

理想情况下,在散列表中查找一个元素的时间复杂度为O(1)

冲突、同义词:

冲突(碰撞):在散列表中插入一个数据元素时,需要根据关键字的值确定其存储地址,若该地址已经存储了其他元素,则称这种情况为 “冲突(碰撞)”

同义词:若不同的关键字通过散列函数映射到同一个存储地址,则称它们为 “同义词”

Q : 如何减少“冲突”?

A : 构造更合适的散列函数,让各个关键字尽可肯地映射到不同的存储位置,从而减少“冲突”

Q : 若”冲突“无可避免,如何处理冲突

A : 用拉链法或者开放定址法

  • 拉链法(又称链接法、链地址法):把所有”同义词“存储在一个链表中

  • 开放定址法:如果发生”冲突“,就给新元素找另一个空闲位置

散列函数的构造

散列函数的构造常用方法:

除留余数法(最常用)

直接定址法

数字分析法

平方取中法

散列函数(哈希函数):Addr=H(key)\text{Addr=H(key)}Addr=H(key)建立了 “关键字”→“存储地址” 的映射关系。

设计散列函数需要注意的问题

  1. 定义域必须涵盖所有可能出现的关键字。
  2. 值域不能超出散列表的地址范围。
  3. 尽可能减少冲突。散列函数计算出来的地址应尽可能均匀分布在整个地址空间。
  4. 散列函数应尽量简单,能够快速计算出任意一个关键字对应的散列地址。

除留余数法

质数的定义:

质数(又称素数)是指在大于 1 的自然数中,除了 1 和它自身外,不能被其他自然数整除的数。

除留余数法 —— H(key)=key%p\text{H(key)} = \text{key} \% \text{p}H(key)=key%p

散列表表长为 m,取一个不大于 m 但最接近或等于 m 的质数 p

适用场景:较为通用,只要关键字是整数即可

例:散列表表长 (m = 13)。13 是质数,因此可令散列函数 H(key)=key%13\text{H(key)} = \text{key} \% 13H(key)=key%13

PS:注意一定取一个不大于 m 但最接近或等于 m 的质数 p

原因:对质数取余,可以分布更均匀,从而减少冲突

直接定址法

直接定址法 —— H(key)=key\text{H(key)} = \text{key}H(key)=keyH(key)=a*key + b\text{H(key)} = \text{a*key + b}H(key)=a*key + b 其中,a 和 b 是常数。这种方法计算最简单,且不会产生冲突。若关键字分布不连续,空位较多,则会造成存储空间的浪费。

适用场景:关键字分布基本连续

数字分析法

数字分析法 —— 选取数码分布较为均匀的若干位作为散列地址

设关键字是 r 进制数(如十进制数),而 r 个数码在各位上出现的频率不一定相同,可能在某些位上分布均匀一些,每种数码出现的机会均等;而在某些位上分布不均匀,只有某几种数码经常出现,此时可选取数码分布较为均匀的若干位作为散列地址。

适用场景:关键字集合已知,且关键字的某几个数码位分布均匀

平方取中法

平方取中法 —— 取关键字的平方值的中间几位作为散列地址。
具体取多少位要视实际情况而定。这种方法得到的散列地址与关键字的每位都有关系,因此使得散列地址分布比较均匀。

适用场景:关键字的每位取值都不够均匀。

拉链法

拉链法(又称链接法、链地址法):把所有 “同义词” 存储在一个链表中

散列表的插入操作

如何在散列表(拉链法解决冲突)中插入一个新元素?

S 1:结合散列函数计算新元素的散列地址
S 2:将新元素插入散列地址对应的链表(可用头插法,也可用尾插法)

散列表的查找操作

S 1:根据散列函数计算目标元素的散列地址
S 2:顺序查找散列地址对应的链表,直到查找成功或查找失败

在分析查找长度时,通常只统计 “关键字的对比次数”,而链表 “空指针的对比次数” 不计入查找长度

散列表的删除操作

S 1:根据散列函数计算目标元素的散列地址
S 2:顺序查找散列地址对应的链表,若查找成功,将目标元素从链表中删除

开放定址法

开放定址法的常用方法:

线性探测法

平方探测法

双散列法

伪随机序列法

开放定址法:如果发生”冲突“,就给新元素找另一个空闲位置

Q : 用什么规则确定“另一个空闲位置”

A :需确定一个“探测的顺序”,从初始化散列表地址出发,去寻找下一个空闲位置。

egegeg :d0=0d_0 = 0d0=0,d1=1d_1 = 1d1=1,d2=−1d_2 = -1d2=1,d3=2d_3 = 2d3=2,d4=−2d_4 = -2d4=2

注意:did_idi表示第 i 次发生冲突时,下一个探测地址与初始化散列表地址的相对偏移量

构造探测序列 di 的四种常用方法
线性探测法
平方探测法
双散列法
伪随机序列法
di = 0, 1, 2, 3, ..., m-1
di = 0^2, 1^2, -1^2, 2^2, -2^2, ..., k^2, -k^2(k <= m/2)
di = i * hash2(key)
di 是一个伪随机序列,如 0, 5, 3, 11, ...

注意 : 0 < = i <= m - 1

Hi=(H(key)+di)%mH_i = (H(\text{key}) + d_i) \% mHi=(H(key)+di)%m

HiH_iHi为发生第 i 次冲突时的散列地址

did_idi为偏移量

mmm为散列表表长

H(key)H(\text{key})H(key)为初始化散列地址

查找操作原理类似,根据探测序列依次对比各存储单元内的关键字

若探测到目标关键字,则查找成功。

若探测到空单元,则查找失败。

散列表的删除操作

步骤:

S 1:先根据散列函数算出散列地址,并对比关键字是否匹配。若匹配,则 “查找成功”
S 2:若关键字不匹配,则根据 “探测序列” 对比下一个地址的关键字,直到 “查找成功” 或 “查找失败”
S 3:若 “查找成功”,则删除找到的元素

  1. 注:采用 “开放定址法” 时,删除元素不能简单地将被删元素的空间置为空,否则将截断在它之后的探测路径,可以做一个 “已删除” 标记,进行逻辑删除。
  2. 注:无论线性探测法、平方探测法、双散列法、伪随机序列法原理都一样。删除元素时,只能逻辑删除

采用 “开放定址法” 删除时,会u带来查找效率低下,散列表看起来很慢,但是实际很空,我们可以采取不定期整理散列表内的数据这个方法来解决问题

散列查找的性能分析

(以线性探测法为例)

**成功查找的平均查找长度(ASL)**ASL成功
ASL成功=1n∑i=1nCi \text{ASL}_{\text{成功}} = \frac{1}{n} \sum_{i = 1}^{n} C_i ASL成功=n1i=1nCi
其中,n 为散列表中已存在的元素个数,(C_i) 为成功查找第 i 个元素所需的比较次数

**失败查找的平均查找长度(ASL)**ASL失败
ASL失败=1r∑i=1rCi \text{ASL}_{\text{失败}} = \frac{1}{r} \sum_{i = 1}^{r} C_i ASL失败=r1i=1rCi

其中,r 为散列函数取值的个数,CiC_iCi 为散列函数取值为 i 时查找失败的比较次数

特别注意:散列函数取值的个数、散列表长度可能不相同

装填因子

散列表的装填因子:α=表中记录数n散列表长度m\alpha = \frac{\text{表中记录数}n}{\text{散列表长度}m}α=散列表长度m表中记录数n,反映了一个散列表“满”的程度

装填因子越大,越容易发生冲突。从而导致插入、查找操作效率降低,ASL 增大

聚集(堆积)现象

聚集(堆积)现象:在处理冲突的过程中,几个初始散列地址不同的元素争夺同一个后继散列地址的现象称作 “聚集”(或称作 “堆积”)

线性探测法在发生冲突时,总是往后探测相邻的后一个单元,很容易造成同义词、非同义词的 “聚集(堆积)现象”,从而影响查找效率,导致 ASL 提升

结论:采用线性探测法处理冲突更容易导致 “聚集(堆积)现象”,用其他方法处理冲突可以将元素 “打散”,从而减少 “聚集(堆积)现象”


文章转载自:

http://VV2EYlQH.qmpbs.cn
http://ydGPMthd.qmpbs.cn
http://VaDMAiXT.qmpbs.cn
http://9WxSbmnm.qmpbs.cn
http://4mKhaCjP.qmpbs.cn
http://UnzYYe6s.qmpbs.cn
http://5r0Z2F87.qmpbs.cn
http://5TH5l8GZ.qmpbs.cn
http://CW8z5CdO.qmpbs.cn
http://VVqix0H7.qmpbs.cn
http://JBmKuQG3.qmpbs.cn
http://ProSQyXM.qmpbs.cn
http://jurRqBSE.qmpbs.cn
http://UVKnTAjV.qmpbs.cn
http://8jJmh0rO.qmpbs.cn
http://a2J83RWj.qmpbs.cn
http://O559O7ye.qmpbs.cn
http://xDTJdl3r.qmpbs.cn
http://ibOKhmnc.qmpbs.cn
http://HdcDP2In.qmpbs.cn
http://zZKf2EmL.qmpbs.cn
http://8YDs9Dkq.qmpbs.cn
http://XGIndGKD.qmpbs.cn
http://jNpHkjNv.qmpbs.cn
http://4paKxBF1.qmpbs.cn
http://PBulTz0x.qmpbs.cn
http://4NdH4LSh.qmpbs.cn
http://kPB4iuHE.qmpbs.cn
http://3SxMsvCE.qmpbs.cn
http://9LZn2L9K.qmpbs.cn
http://www.dtcms.com/a/385691.html

相关文章:

  • 【Redis】-- 主从复制
  • 输入1.8V~5.5V 输出28V DCDC升压芯片TLV61046A
  • Windows 上安装 FFmpeg 8.0(2025 版)——从“手动解压”到“一条命令”的进化之路
  • 红黑树(RBTree)知识总结
  • 若依框架前端通过 nginx docker 镜像本地运行
  • 二十、瑞萨RZT2N2 PROFINET SDK正式发布
  • SpringAI框架接入Deepseek和豆包实现智能聊天
  • 江协科技STM32课程笔记(一) —GPIO
  • 江协科技STM32课程笔记(二)—外部中断EXTI
  • 科技信息差(9.15)
  • 龙珠KS6 10.5T矿机评测:性能、功耗、噪音与冷却分析
  • 打工人日报#20250915
  • 新一代车载诊断框架简介
  • 05-索引-性能分析
  • 【数据工程】 2. Unix 基础与文件操作
  • 第四课、 TypeScript 中 Cocos 的生命周期
  • 联邦学习论文分享:DPD-fVAE
  • Pairwise排序损失:让机器学会排序的艺术
  • 硬件开发—IMX6ULL裸机—UART通信
  • 蓝牙上位机开发指南
  • 【课堂笔记】复变函数-1
  • 谈谈人大金仓数据库
  • C#调用钉钉API发送通知教程
  • 电子电气架构 --- 产线EOL为何需要智能升级?
  • 无人机姿态控制系统详解与实现
  • 7.Redis 主从复制(重在理解)
  • 从零搭建RAG应用:跳过LangChain,掌握文本分块、向量检索、指代消解等核心技术实现
  • 从0开始做一个完整项目 -- 软件跨平台编译打包全流程
  • comfyUI实战——使用openArt的工作流
  • linux 之 struct attribute