数据结构 第六章 查找——散列表,散列查找

本文深入解析散列查找方法,涵盖散列函数构造、冲突处理策略、开放地址法与链地址法,以及散列表性能分析等内容,助您掌握高效查找技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

已知的几种查找方法:
(1)顺序查找:O(n);
(2)二分查找(静态查找):O(logn);
(3)二叉搜索树:O(h)(h为二叉查找树高度);
(4)平衡二叉树:O(logn)。

查找的本质:已知对象找位置:
(1)通过有序的安排对象:全序(顺序),半序(查找树,左子树小于根结点,右子树大于根结点);
(2)直接算出对象位置:散列表。

散列查找的两项基本工作:
(1)计算位置:构造散列函数确定关键词存储位置;
(2)解决冲突:应用某种策略解决多个关键词位置相同的问题。

散列表(哈希表):(根据关键字直接进行访问的数据结构)
(1)类型名称:符号表(SymbolTable);
(2)数据对象集:符号表是“名字(Name)——属性(Attribute)”对的集合。
(3)操作集:之后来补,考纲上没有看到要求。

装填因子(Loading Factor):设散列表空间大小为m,填入表中的元素个数是n,则n/m为散列表的装填因子。
散列查找的时间复杂度几乎是O(1),查找规模与问题的规模n无关。

散列(Hashing)的基本思想是:
(1)以关键字key为自变量,通过一个确定的函数h(散列函数),计算出对应的函数值h(key),作为数据对象的存储地址;
(2)可能不同的关键字会映射到同一个散列地址上,即h(key i)= h(key j),但两个关键字并不相等,称为冲突(collision)(不可避免但要减少),把具有相同函数值的关键字称为同义词。

散列函数:建立表中关键字到散列地址的映射关系。即hash(key) = address。

散列函数的构造方法:
(1)散列函数应尽可能计算简单,以便提高转换速度;
(2)关键字对应的地址空间应均匀分布,而减少冲突的发生;
(3)散列函数的定义域应包含所有要存储的的关键字。

数字关键字的散列函数构造:
(1)直接定址法:取关键字的某个线性函数值为散列地址,散列函数为:
h(key) = a * key + b,这种方法计算简单,不会产生冲突(因为线性函数只要自变量不同,函数值就不同),适合关键字分布基本连续的情况,若关键字分布不连续,则空间效率比较低,有大量的空位没有元素;
(2)除留余数法:h(key) = key mod p(p一般取素数,p不大于散列表的长度TableSize,一般选大于等于TableSize的最小的素数);
(3)数字分析法:分析数字在各位上的变化情况,选取其中变化比较随机的位作为散列地址(随机性比较好对应每位数字出现的概率相同,也就对应了它们在散列表中分布比较均匀),这种方法适合于已知的关键字集合(例如手机号,身份证号存储和查找)。
(4)折叠法:把关键字分成位数相同的几部分,然后叠加作为散列地址,这样做是为了能够让关键字的每一位都参与到构造散列地址中去,能够减少冲突,这个方法适合于关键字位数比较多,且数字在每位上分布大致均匀的情况;
(5)平方取中法:取关键字平方的中间几位作为散列地址,原因和折叠法相似,都是为了能够让关键字的每一位都参与到构造散列地址中去来减少冲突的发生。

字符串为关键字的散列函数构造:
(1)ASCII码加和法,容易产生冲突;
(2)前三个字符移位法,仍然有冲突,且空间效率不高;
(3)好的散列函数是移位法中涉及所有n个字符并且分布的很好。

影响散列查找时间的因素:
(1)散列函数执行速度(即计算散列值所需的时间);
(2)关键字的长度;
(3)散列表的大小;
(4)关键字的分布情况;
(5)不同关键字的查找频率。

冲突处理的方法:
(1)给冲突的对象换个地址:开放地址法。
(2)把同一位置冲突的对象组织在一起:链地址法。

开放地址法(Open Addressing):指可存放新表项的空地址既向他们的同义词开放,又向他们的非同义词开放,一旦产生了冲突(该地址已有其他元素),则按照某种规则去寻找(试探)另一空地址,若发生了第i次冲突,试探的下一个地址将增加di,即hi(key) = ( h(key) + di ) mod TableSize。
增量序列di决定了不同的解决冲突方案:
(1)线性探测法(Linear Probing):
增量序列di以1, 2, 3, ……,TableSize - 1循环试探下一个存储地址,缺点是会产生聚集现象,大大降低了查找效率。
(2)平方探测法(Quadratic Probing)(二次探测法):
增量序列以1^2, -1 ^2, 2 ^2, -2 ^2, ……,q ^2, -q ^2(q <= TableSize / 2 )循环试探下一个存储地址。
在某一些条件下,可能会出现试探左右跳来跳去始终无法找到正确位置的情况,即可能存在无法探测到散列表中所有元素的情况,所以:
有定理显示:如果散列表长度TableSize是某个4k+3(k是正整数)形式的素数时,平方探测法就可以探查到整个散列表空间。
平方探测法的实现后续来补。
在开放地址散列表中,删除操作要很小心,通常只能“懒惰删除“,即需要增加一个删除标记(Deleted),而并不是真正的删除它(因为真正的删除它会打破散列表的分布,破坏其它具有相同散列地址的元素的查找),以便在查找时不会断链,其空间可以在下次插入时重用。
(3)双散列探测法(double Hashing):di = i * h2(key),其中,对任意的key,都有h2(key) != 0, 探测序列还应该保证所有的散列存储单元都能够被探测到,选取h2(key) = p - ( key mod p )有良好的效果,其中p < TableSize, p,TableSize都是素数;
(4)再散列(Rehashing):当散列表元素太多(即装填因子过大时),查找效率会降低(因为在查找时冲突次数会变多),这时的解决方法就是扩大散列表,然后把所有元素重新计算后放在新表里(实用最大填充因子一般介于0.5到0.85之间)。
(5)伪随机序列法:当di为伪随机序列时,称为伪随机序列法。

链地址法(分离链接法)(Separate Chaining):把相同位置上冲突的所有关键字存储在同一个单链表中。

散列表性能分析:
平均查找长度(ASL)用来衡量散列表查找效率:
(1)ASL s:成功平均查找长度;
(2)ASL u:不成功平均查找长度。

关键字的比较次数,取决于产生冲突的多少。
影响产生冲突多少的因素:
(1)散列函数是否平均;
(2)处理冲突的方法;
(3)散列表的装填因子。

(1)线性探测的期望探测次数:

(2)平方探测法与双散列探测法的期望探测次数:

当装填因子小于0.5时,各种探测法的探测次数都不大,也比较接近,合理的最大装填因子应不超过0.85.

(3)链地址法期望探测次数:
链地址法的装填因子定义为所有地址链表的平均长度(可能会超过1)。

散列方法是以空间换时间的方法。散列方法的存储对关键字是随机的,所以不便于顺序查找关键字,不适合于范围查找,也不适合于最大值最小值查找。

(1)开放地址法:优点是散列表是一个数组,存储效率高,随机查找,缺点是有聚集现象;
(2)链地址法:散列表是顺序存储和链式存储的结合,链表部分的存储效率和查找效率都比较低。而且关键字的删除不需要懒惰删除,所以不会存储垃圾。填充因子太小会导致空间浪费,填充因子太大会导致付出更多的时间代价,不均匀的链表长度导致时间效率的严重下降。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

房东的小黑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值