Hash冲突解决方法

本文介绍了哈希冲突的概念及常见的解决方法,包括开放寻址法、链地址法、再哈希法和建立公共溢出区等。每种方法都有详细的解释和示例,帮助读者深入理解如何有效解决哈希冲突。

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

目录

1.基本概念

2.解决哈希冲突的方法

2.1 开放寻址法

2.1.1 线性探查法

2.1.2 平方探查法(二次探测法)

2.1.3 双散列函数探查法

2.1.4 伪随机探查法

2.1.5 小结

2.2 链地址法(拉链法)

2.3 再哈希法

2.4 建立公共溢出区


1.基本概念


哈希算法:根据设定的哈希函数H(key)和处理冲突方法将一组关键字映象到一个有限的地址区间上的算法。也称为散列算法、杂凑算法。

哈希表:数据经过哈希算法之后得到的集合。这样关键字和数据在集合中的位置存在一定的关系,可以根据这种关系快速查询。

非哈希表:与哈希表相对应,集合中的数据和其存放位置没任何关联关系的集合。

由此可见,哈希算法是一种特殊的算法,能将任意数据散列后映射到有限的空间上,通常计算机软件中用作快速查找或加密使用。

哈希冲突:由于哈希算法被计算的数据是无限的,而计算后的结果范围有限,因此总会存在不同的数据经过计算后得到的值相同,这就是哈希冲突。

2.解决哈希冲突的方法


解决哈希冲突的方法一般有:开放寻址法、链地址法(拉链法)、再哈希法、建立公共溢出区等方法。

2.1 开放寻址法


开放寻址法又叫做开放定址法、开地址法,从发生冲突的那个单元起,按照一定的次序,从哈希表中找到一个下一个空闲的单元。然后把发生冲突的元素存入到该单元的一种方法。开放定址法需要的表长度要大于等于所需要存放的元素。

一旦产生冲突,就去寻找下一个空的哈希地址。只要哈希表足够大,空的哈希地址总能找到,并将数据元素存入。其数学递推公式为

 

式中,i=1 , 2 , ... , k(k<=m-1);m表示哈希表表长; 为增量序列。
 

在开放定址法中根据探查序列生成方式的不同,细分有:线性探查法、平方探查法(二次探测法)、双散列函数探查法、伪随机探查法等。

开放定址法的缺点在于删除元素的时候不能真的删除,否则会引起查找错误,只能做一个特殊标记。只到有下个元素插入才能真正删除该元素。

2.1.1 线性探查法


线行探查法是开放定址法中最简单的冲突处理方法,它从发生冲突的单元起,依次判断下一个单元是否为空,当达到最后一个单元时,再从表首依次判断。直到碰到空闲的单元或者探查完全部单元为止。

当=1 , 2 , ... , m-1时称为线性探测法。其特点为:发生冲突时,顺序查看表中的下一个单元,当探测到表位地址m-1时,下一个探测地址是表首地址0,直到找出一个空闲单元(当表尾填满时一定能找到一个空闲单元)或查遍全表。

例如,哈希表表长为10,以关键字的末尾数字作为哈希地址,依次插入45、22、13、65、29、42、79、2共8个记录。若发生冲突则采用线性探测法。

1)首先先插入45,22,13,由于是在哈希表为空的时候插入,就直接插入在对应的地址上。(图a)

 

2) 在图a的基础上插入65,29,42,在插入42和65时,都发生了冲突,然后就都依次往下一个单元走,若有空闲单元则直接插入;若没有空闲单元,则继续往下一个单元走,直到找到空闲单元。(图b)

 

 3)  在图b的基础上插入79,2,原理是跟上面一样的。但在插入79时,发生冲突,该位置为哈希表的表尾,所以就只能从哈希表的表首开始找空闲单元。(图c)

 

4)  在图c的基础上插入64,在插入64时,该位置已经被42给占了,但是这个位置原本不属于42的,像这种情况我们成为“堆积”。然后只能往下一个单元走,直到找到空闲单元。(图c)

 

2.1.2 平方探查法(二次探测法)


平方探查法即是发生冲突时,用发生冲突的单元 d[i], 加上 1²、 2² 等。即 d[i] + 1²,d[i] + 2², d[i] + 3²… 直到找到空闲单元。

在实际操作中,平方探查法不能探查到全部剩余的单元。不过在实际应用中,能探查到一半单元也就可以了。若探查到一半单元仍找不到一个空闲单元,表明此散列表太满,应该重新建立。

当,其中k<=m/2,m必须是一个可以表示成4k+3的质数时,称为二次探测法,又称平方探测法。二次探测法是一种较好的处理冲突的方法,可以避免出现“堆积”问题,它的缺点是不能探测到哈希表上的所有单元,但至少能探测到一半的单元。

例如,若有关键字集合{47,7,29,11,16,92,22,8,3}, 哈希表表长为11,哈希函数为H(k)=k%11,用二次探测法处理冲突,得到的哈希表如图2.2.2,写到3发生冲突。

 

         为关键字寻找空的哈希地址时只有关键字3与线性探测法不同,H(3)=3,哈希地址发生冲突,有,仍然冲突;继续下一步,找到空的哈希地址,将3存入。

 

 

2.1.3 双散列函数探查法


双散列函数探查法又叫做双重散列探查法(出自算法导论),是开发寻址法中的最好方法之一,因为它所产生的探查序列具有随机性。

关于叫法推荐叫双散列函数探查法,因为双重散列探查法的名字有歧义,是使用两个散列函数还是使用一个散列函数做两次散列计算呢,没有那么直白。

这种方法使用两个散列函数 h1 和 h2。其中 h1 和前面的 h 一样,以关键字为自变量,产生一个 0 至 m-1 之间的数作为散列地址;h2 也以关键字为自变量,产生一个 1 至 m-1 之间的并和 m 互素的数(即 m 不能被该数整除)作为探查序列的地址增量(即步长)。这样做是使探查序列能够分布在整个 Hash 表。

2.1.4 伪随机探查法


具体实现时,建立一个伪随机数发生器来生成探查序列。

例如,假设哈希表长度 m=11,哈希函数为:H(key)= key % 11,则 H(47)=3,H(26)=4,H(60)=5,假设下一个关键字为 69,则H(69)=3,与 47 冲突。如果用伪随机探测再散列处理冲突,且伪随机数序列为:2,5,9,…,则下一个哈希地址为 H1=(3+2)%11=5,仍然冲突,再找下一个哈希地址为 H2=(3+5)%11=8,此时不再冲突,将 69 填入 8 号单元。

2.1.5 小结


四种不同的开放寻址法,根据其探查序列可以看出,线性探查法的步长值固定为 1;平方探查法步长值是探查次数 i 的两倍减 1;双散列函数探查法,其探查序列的步长值是同一关键字的另一散列函数的值。对于伪随机探查法,探查序列是随机的,所以步长也是随机的。

2.2 链地址法(拉链法)


链地址法(Separate Chaining)的思路是将哈希值相同的元素构成一个同义词的单向链表,并将单向链表的头指针存放在哈希表的第 i 个单元中,查找、插入和删除主要在同义词链表中进行。即将相同hash值的对象组织成一个链表放在hash值对应的槽位。

如下一组数字:(32、40、36、53、16、46、71、27、42、24、49、64),哈希表长度为13,哈希函数为 H(key)=key%13,则链表法结果如下:

0       
1  -> 40 -> 27 -> 53 
2
3  -> 16 -> 42
4
5
6  -> 32 -> 71
7  -> 46
8
9
10 -> 36 -> 49
11 -> 24
12 -> 64
注意: 链地址法是主流开发语言中 HashMap 冲突的解决办法,如 Java、Go 等。以 Java 为例,JDK1.7 完全采用单链表来存储同义词,JDK 1.8 则采用了一种混合模式,对于链表长度大于 8 的,会转换为红黑树存储。

2.3 再哈希法


就是同时构造多个不同的哈希函数:

Hi = RHi(key) i= 1,2,3 ... k;


其中 RHi 为不同的哈希函数。当 H1 = RH1(key) 发生冲突时,再用 H2 = RH2(key) 进行计算,直到冲突不再产生,这种方法不易产生聚集,但是增加了计算时间。

2.4 建立公共溢出区


将哈希表分为公共表和溢出表,当溢出发生时,将所有溢出数据统一放到溢出区。


————————————————

为方便理解,两篇文件融合修改,如侵权,请联系删除。

原文链接1: 哈希冲突常用解决方法

原文链接2:Hash冲突以及如何解决Hash冲突

附带:hashmap hash冲突怎么解决_HashMap原理(建议观看)
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值