<JavaDS> 哈希冲突/碰撞 的避免和解决方式

目录

一、哈希表

1.1 哈希表的概念

1.2 使用哈希表的实现类

1.3 哈希函数

1.4 哈希冲突/哈希碰撞

二、避免--哈希冲突

2.1 哈希函数的设计

2.2 调节负载因子

三、解决--哈希冲突 

3.1 闭散列 方法 

3.2  开散列/哈希桶 方法


一、哈希表

1.1 哈希表的概念

        哈希表(Hash Table)是一种用于存储数据的结构,通过某种函数(hashFunc)使元素的存储位置与它的关键码之间能够建立一一映射的关系,这使得哈希表能够在平均情况下以非常快速的速度进行插入和检索操作。哈希表又称为 散列表 。


1.2 使用哈希表的实现类

        底层结构为哈希桶的实现类包括HashSet和HashMap等,两者的区别在于

        Set是继承于Collection的接口类,存储的只是key值,且key是唯一的;

        Map不继承于Collection,存储的是key-val模型,key是唯一的,val是可以重复的;

                key 一般称为关键字(Key),val 和关键字一一对应,一般称为值(value)。key 和 val 组合为 key - val 模型 ,称为键值对。


1.3 哈希函数

         哈希函数是指,可以将 key 值转换为哈希值的函数,哈希值确定了该值应在哈希表中存储的位置。

        例如将哈希函数设置为:哈希值 = key % 数组容量;


1.4 哈希冲突/哈希碰撞

        不同关键字通过相同哈希函数计算出相同的哈希地址,该种现象称为哈希冲突或哈希碰撞。

        假设有一组整数数据 {1,2,3,22 },根据上文哈希函数进行哈希值计算,以确定数据在哈希表中的存储位置。


二、避免--哈希冲突

        哈希冲突无法避免,但可以通过两个方面降低哈希冲突发生的可能性,一个是设计更精妙的哈希函数,另一个是调节负载因子。

2.1 哈希函数的设计

        哈希函数的设计方法有许多种,通常采用直接定制法除留余数法,根据不同的使用场景,还有平方取中法、折叠法、随机数法和数学分析法等。

哈希函数设计原则包括

  • 哈希函数的定义域必须包括需要存储的全部关键码,而如果散列表允许有m个地址时,其值域必须在0到m-1 之间。
  • 哈希函数计算出来的地址能均匀分布在整个空间中。
  • 哈希函数应该比较简单。

直接定制法:以关键字的值作为参数,设计关于这个参数的线性函数作为哈希函数,这种方法设计简单,适用于查找比较小且连续的情况。

        如:Hash(Key)= A*Key + B

除留余数法:取一个最接近且小于哈希表大小的质数 p 作为除数,关键字的值作为被除数,设计出哈希函数,将关键码转换成哈希地址。

        如:Hash(key) = key % p(p<=m) 


2.2 调节负载因子

        负载因子是指,填入哈希表的key关键字个数和哈希表长度的比值,即:

                负载因子 α = 哈希表key关键字个数/哈希表长度

哈希表负载因子和冲突率关系图

        由上图可知,负载因子越高,哈希地址冲突率越高,而负载因子的参数 “key关键字个数” 是不可变的,因此为了调节负载因子,只能在负载因子达到不可接受的数值(Java规定该值为0.75)的时候,调整哈希表的长度。

        应注意,哈希函数的设计中,通常会将 “哈希表长度” 作为参数之一,并以此计算哈希地址。当哈希表的长度发生变化时,元素的哈希地址也可能会发生变化。因此,在每次调整负载因子时,哈希表中已经存储的元素也需要全部重新计算哈希地址


三、解决--哈希冲突 

3.1 闭散列 方法 

        闭散列方法又称开放地址法,当发生哈希冲突时,如果哈希表仍存在空位置,或负载因子尚未达到不可接受的程度,那么可以把 key值 以某种方式重新定位哈希地址,存放到其他不冲突的空位置中。

        重新定位哈希地址的方式包括:线性探测和二次探测

线性探测是指:从发生冲突的位置开始,依次向后探测,直到寻找到下一个空位置为止。但该种方法存在元素堆积的缺点,不能使元素均匀分布在整个空间中。

二次探测是指:如果发生冲突,则通过公式计算得到空位置存放,而不是直接找到下一个空位置这样“简单粗暴”。该种方法使得元素的分布更加平均。

        公式:Hi = (H0 ± i^2) % m

        其中:i = 1,2,3…,H0是通过散列函数Hash(x)对元素的关键码 key 进行计算得到的位置, m是哈希表的大小。 

闭散列方法的缺点:

        采用闭散列处理哈希冲突时,不能随便物理删除哈希表中已有的元素,若直接删除元素会影响其他元素的搜索。因此线性探测采用标记的伪删除法来删除一个元素。

        闭散列方法无论采取哪种探测方式计算哈希地址,最终都要将元素存放到有限的空间中,这就导致了闭散列方法的最大的缺陷,空间利用率比较低,这也是哈希的缺陷


3.2  开散列/哈希桶 方法

        开散列法又叫链地址法或开链法,首先对关键码集合用散列函数计算散列地址,具有相同地址的关键码归于同一子集,每一个子集合称为一个桶,各个桶中的元素通过一个单链表或红黑树链接起来,各链表的头结点存储在哈希表中,因此又将该种方法称为哈希桶。

        开散列方法将冲突元素全部归于一个子集中,变相使哈希表的空间利用率最大化。

        在Java中就是使用哈希桶的方式解决哈希冲突的问题。发生哈希冲突后会将元素归于一个子集,并使用链表连接各个元素当链表元素到达8个且数组长度大于64时,会将链表转化为红黑树存储数据,以提高查询效率。


( 哈哈哈~~ 文章结束!) 

( 看到这里,如果有为各位帅哥美女提供一点点灵感,请点一个小小的赞哦,比心💖💖💖 )

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值