【数据结构】散列查找

部分copy散列查找by萝卜头LJW以及浙大数据结构视频学习后得出

why

为什么要用散列查找?

what

什么是散列查找?

解决冲突有哪些方法?

how

如何评估查找?

散列函数如何设计?


为什么要用散列查找?

顺序查找 时间复杂度 O(N)

二分查找(前提有序) 时间复杂度 O(logN)

二叉搜索树 时间复杂度 O(h),h 是树高,最好情况 h = logN,最差 h = N 。

二叉平衡树 时间复杂度 O(logN)

如何数据量很大时怎么办,10 亿,100 亿 ?

顺序查找全部遍历不可能 ,二分查找要求有序太难了,2 的 30 次方等于10 亿多,那么二叉搜索树、二叉平衡树最好情况下树高 30,比 30 次也有点多。

散列查找:通过散列函数的计算求出关键字的位置。 这样就有很快的查找效率。时间复杂度几乎是常量 O(1)。


什么是散列查找?

散列 (Hashing) 是一种重要的查找方法。它的基本思想是:以数据对象的关键字 key 为自变量,通过一个确定的函数关系 h,计算出对应的函数值 h(key) ,把这个值解释为数据对象的存储地址(可能不同的关键字会映射到同一个散列地址上会有冲突,需要解决),并按此存放,即“存储位置= h(key)”。

散列表(Hash Table)也称为哈希表。

散列查找的两项基本工作:

1.计算位置构造散列函数确定关键字存储位置
2.解决冲突应用某种策略解决多个关键词位置
时间复杂度几乎是常量O(1),即查找时间与问题规模无关


散列函数的构造方法

散列函数的设计需要考虑下列两个因素:

1.计算简单,以便提高转换速度
2.关键词对应的地址空间分布均匀,以尽量减少冲突

数字关键词的散列函数构造

1.直接定址法

取关键词的某个线性函数值为散列地址,即h(key) = a × key + b (a、b为常数)
例:想要统计1990~2011年出生的人数,可以设a = 1,b = 1990,可设计成如下形式:
h(key) = key - 1990
在这里插入图片描述

2.除留余数法

散列函数为:h(key) = key mod p
p一般取素数
例:
TableSize = n / α, n 是关键词集合大小,α 是允许最大装填因子

一般 p 取素数,p <= TableSize

是现实应用中比较常见的方法。

3.数字分析法

如果数字关键词的位数比较多,取比较随机变化较多的位作为散列地址。
例:特定的一系列手机号,它们主要是手机尾号后四位进行变化,那么可以取手机号码key的后四位作为地址
散列函数为h(key) = atoi(key+7)
int atoi(char* s) :将数字字符串转成数字
在这里插入图片描述
4.折叠法

把关键词分割成位数相同的几个部分,然后叠加
如:对于一串数字,从个位数开始3位3位取,不足的补0,再将这些三位数相加,取结果的三位数为key
如56793542,542+793+056 = 1391
那么h(56793542) = 391即key

5.平方取中法

如56793542,将其平方,再取中间的三位数作为key
56793542×56793542 = 3225506412905764
h(56793542) = 641


字符关键词的散列函数构造

1.ASCII码加和法

对字符关键词 key 定义散列函数如下:

h(key) = (∑key[i]) mod TableSize

函数简单,均匀性比较差,冲突比较严重(如a3,b2,c1;eat,tea这些)。

2.简单的改进————前 3 个字符移位法

h(key) = (key[0] * 27^2 + key[1] * 27 + key[2] ) mod TableSize

3.好的散列函数————移位法

涉及关键词所有 n 个字符,并且分布得很好:
在这里插入图片描述
如:在这里插入图片描述
如果要代码实现的话,大致思想是:
{{[(a) * 32 + b]*32+c}*32+d}*32+e
一层一层乘上32
这些方法并不是完美的,无论是哪一种,总会找到一组反例数据导致key一样,这就是冲突,接下来就需要处理这些冲突。


冲突处理的方法

常用处理冲突的思路:

换个位置: 开放地址法
同一位置的冲突对象组织在一起: 链地址法

开放地址法

一旦产生了冲突(该地址已有其它元素),就按某种规则去寻找另一空地址
若发生了第 i 次冲突,试探的下一个地址将增加 di,基本公式是:
在这里插入图片描述
不同的di决定了不同的解决冲突方案:线性探测、平方探测、双散列。

线性探测
以增量序列1,2……(TableSize - 1)循环试探下一个存储地址。即di = i
在这里插入图片描述
1.先算出散列地址,直接求h(key)
2.模拟进行插入操作。
对冲突情况说明:如插入29,h(29) = 7,那么此时d1=1,移到地址为8的地方即可。
如插入30,此时地址为8的地方已经被占用,因此di++,一直加到d4,位置在12,依然冲突,再加的话需要对其mod TableSize,直到d6处可以放下,那就可以放入。
在这里插入图片描述
在这里插入图片描述
ASL s 中的值每个数的查找次数,如对于11,查找1次就查找到了(11 mod 11 = 0),对于30,需要查找7次(30 mod 11 = 8,再d6走6次,共7次),分母是放入地址中的数字总数
ASL u中的值是对于H(key)相同的值,找不到它的情况(并不一定要列出完全相同的例子),如22,H(22) = 0,从地址0开始,没有,到1,也不是,到2发现此处是空的,那就断定没有22,其它类似。分母是自行设置的b(mod 11的11)

平方探测——二次探测
在这里插入图片描述
在这里插入图片描述
di = ±i²
实际上就是冲突地点开始左右横跳
缺陷:在一定特殊情况下,表内有空间,但是平方探测找不到改下放的地址
在这里插入图片描述
探测序列一直是2和0,3和4够不着
有定理显示:如果散列表长度TableSize是某个4k+3(k是正整数)形式的素数时,平方探测法就可以探查到整个散列表空间

双散列探测法
d1为i*h2(key),h2(key)是另一个散列函数
探测序列成h2(key),2h2(key),3h2(key)……
对任意的key,h2(key)≠0
探测序列还应该保证所有的散列存储单元都应该能够被探测到。选择以下形式有良好的效果:
h2(key) = p - (key mod p)
其中p < TableSize,p、TableSize都是素数

再散列
当散列元素太多(即装填因子α太大)时,查找效率会下降;解决的方法是加倍扩大散列表,这个过程叫再散列。
散列表扩大时,原有元素需要重新计算放置到新表中
实用最大装填因子一般取0.5 <= α <= 0.85

分离链接法
将相应位置上冲突的所有关键词存储在同一个单链表中
在这里插入图片描述

  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值