vlambda博客
学习文章列表

哈希算法高大上?也不过如此


01

知识框架



02

知识点详解

1

散列表的相关概念


①什么是散列表和散列函数?


是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,记为 Hash(key) = Addr 这里的地址是数组下标、索引或内存地址等)。 

存放记录的数组叫做散列表。

②什么是同义词和冲突?


散列函数可能会把两个或两个以上的不同关键字映射到同一地址,称这种情况为冲突,这些发生碰撞的不同关键字称为同义词。一方面,设计得好的散列函数应尽量减少这样的冲突;另一方面,由于这样的冲突总是不可避免的,所以还要设计好处理冲突的方法。

2

散列函数


下面介绍几种常见的散列函数:


①直接定址法


取关键词的某个线性函数值为散列地址,即H(key) = a × key + b (a、b为常数)

适用情况:它适合关键字的分布基本连续的情况,若关键字分布不连续,空位较多,则会造成存储空间的浪费。

②除留余数法


散列函数为:H(key) = key mod p(假定散列表的长度为m,取一个不大于m但是接近于m的质数p)

它是一种最简单、最常见的方法。

③数字分析法


分析数字关键字在各位上的变化情况,取比较随机的位作为散列地址。

适用情况:这种方法适合于己知的关键字集合,若更换了关键字,需要重新构造新的散列函数。

④平方取中法


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

适用情况:适用于关键字的每位取值都不够均匀或均小于散列地址所需的位数。

3

散列函数


①开放定址法


开放定址法就是一旦发生冲突,就去寻找下一个空的散列地址。它的递推公式为:
  
    
    
  
Hi= (H(key) + di) % m

其中H(key)为散列函数;i=0,1,2,...,k(k<=m-1);m表示散列表的长度;di表示增量序列。

这里增量序列的不同表示处理方式也不同,增量序列一旦被确立之后,处理方式也就确立了。通常,增量序列有以下四种取值方式:


(1) 

线性探测法:当di=0,1,2,..,m-1时,称为线性探测法。


探测方式: 冲突发生时,顺序查看表中下一个单元(探测到表尾地址m- 1时,下一个探测地址是表首地址0),直到找出一个空闲单元(当表未填满时一定能找到一个空闲单元) 或查遍全表。

缺点: 线性探测法的探测方法会使大量元素在相邻的散列地址上”堆积”起来,这样大大降低了查找效率。

(2) 
平方探测法: 当di=0,12,-12,22,-22,...,k2, -k2时,称为平方探测法,其中k<=m/2,散列 表长度m必须是一个可以表示成4k+ 3的素数,这样的方法又称二次探测法。

探测方式: 与线性探测法类似。但它可以避免出现“堆积”问题。

缺点: 由于di为某数的平方,不能探测到散列表上的所有单元,但至少能探测到一半单元。

(3) 
再散列法: 当di= Hash2(key)时,称为再散列法,又称双散列法。需要使用两个散列函数。

探测方式: 当通过第一个散列函数H(key)得到的地址发生冲突时,则利用第二个散列函数Hash2(key)计算该关键字的地址增量。它的具体散列函数形式如下:
  
    
    
  
Hi= (H(key) + i×Hash2(key)) % m
初始探测位置H0= H(key)%m。i是冲突的次数,初始为0。

(4) 
伪随机序列法: 当di=伪随机数序列时,称为伪随机序列法。

缺点: 用同样的随机种子,将得到相同的数列。

开放定址法缺点:

① 使用探测序列,有时计算的时间成本过高,导致散列表的处理性能降低

② 删除记录时较麻烦。不能随便物理删除表中的已有元素,因为若删除元素,则会截断其他具有相同散列地址的元素的查找地址。因此,要删除一个元素时,可给它做一个删除标记, 进行逻辑删除。但这样做的副作用是执行多次删除后,表面上看起来散列表很满,实际上有许多位置未利用,因此需要定期维护散列表,要把删除标记的元素物理删除。


所有关键字为同义词的记录存储在一个单链表中,称这种表为同义词子表,在散列表中只存储所有同义词子表的头指针。拉链法适用于经常进行插入和删除的情况。

优点:


① 对于记录总数频繁可变的情况,处理的比较好。


② 删除记录比较方便,直接通过指针操作即可。



缺点:

存储的记录是随机分布在内存中的,这样在查询记录时,相比结构紧凑的数据类型(比如数组),散列表的跳转访问会带来额外的时间开销。

4

性能分析


①平均查找长度


(1) 
查找成功时的平均查找长度是指找到表中已有表项的平均比较次数,它是找到表中各个已有表项的平均比较次数。

(2)  
查找不成功的平均查找长度是指在表中找不到待查的表项,但找到插入位置的平均比较次数。它是在表中所有可能散列到的地址,上插入新元素时,为找到空位置而进行探查的平均次数。


②装填因子


哈希算法高大上?也不过如此


Tips:散列表的平均查找长度与关键字个数n无关,而与装填因子a有关


下面我们来看一个例子:


Q:


用关键字序列{2, 34,23,11, 25, 35,12,14}创建一个Hash表,装填因子a为1/2,试确定表长m,采用除留余数法构造Hash函数,采用链地址法来处理冲突,并计算查找成功与不成功时的平均查找长度ASL1和ASL2(只将与关键字的比较计算在内)。

A:

(1) 
由装填因子a的定义知道,a=n/m, 其中n为关键字个数,m为表长,因此可以求出m为16。

(2) 
除留余数法的Hash函数构造公式为H(key)=key mod p,其中p为不大于表长的最大素数,因表长m为16,所以p取13,Hash函数为H(key)=key Mod 13。

(3) 
用(2)中构造的Hash函数并用链地址法处理冲突所建立的Hash过程如下:

2 mod 13 = 2,将2插在地址为2的链表表尾
34 mod 13 = 8,将34插在地址为8的链表表尾
23 mod 13 = 10,将23插在地址为10的链表表尾
11 mod 13 = 11,将11插在地址为11的链表表尾
25 mod 13 = 12,将25插在地址为2的链表表尾
35 mod 13 = 9,将35插在地址为9的链表表尾
12 mod 13 = 12,将12插在地址为12的链表表尾
14 mod 13 = 1,将14插在地址为1的链表表尾

得到如下散列表:


哈希算法高大上?也不过如此


(4)  
求ASL1
ASL1=(1+1+2+1+1+1+1+1)/8=1.125

关键字比较次数:

哈希算法高大上?也不过如此

(5) 
求ASL2
ASL2=(0+1+2+0+0+0+0+0+1+1+1+1+1)/13=0.615

地址比较次数:

哈希算法高大上?也不过如此

03

相关习题


01

( 2014统考)用哈希(散列)方法处理冲突(碰撞)时可能出现堆积(聚集)现象, 下列选项中,会受堆积现象直接影响的是()



A、存储效率


B、散列函数


C、装填(装载)因子


D、平均查找长度


(点击选项查看答案)

Tips: 产生堆积现象,即产生了冲突。平均查找长度会因为堆积而增大。

02

(2018统考真题)现有长度为7、初始为空的散列表HT,散列函数H(k)=k % 7,用线性探测再散列法解决冲突。将关键字22, 43, 15 依次插人到HT后,查找成功的平均查找长度是()。



A、1.5


B、 1.6


C、2


B、与时俱进


C、实事求是


D、持续稳定


D、3


(点击选项查看答案)

Tips: 根据题目要求可以画出散列表,根据散列表可以得到ASL成功=(1+2+3)/3=2

03

(2019统考真题)现有长度为 11且初始为空的散列表HT,散列函数是H (key)=key%7,采用线性探查(线性探测再散列)法解决冲突。将关键字序列87, 40, 30, 6, 11,22, 98, 20依次插入HT后,HT查找失败的平均查找长度是()。



A、4


B、5.25


C、6


B、与时俱进


C、实事求是


D、持续稳定


D、6.29


(点击选项查看答案)

Tips:由于H(key)=0~6,查找失败时对应的可能地址有七个,要特别注意的是,散列函数不可能计算出地址7。


故ASL失败=(9+8+7+6+5+4+3)/7=6


哈希算法高大上?也不过如此

抓码计算机考研qq群

总群:625590924

广大:1143982604

暨大:1071137230

广工:938111325

华工:428389734

深大:729770764

浙大:978938582

厦大:1125268501

中大:921801084

南航:281118241

华农:515681663

重邮:736197896

北邮:1126650806

南邮:1109929146

广外:976231252

东北大学:1128523098

华南师大:428389734

南昌大学:923249141



给个“在看”支持一下我