hash冲突的四种办法


一)哈希表简介

非哈希表的特点:关键字在表中的位置和它之间不存在一个确定的关系,查找的过程为给定值一次和各个关键字进行比较,查找的效率取决于和给定值进行比较的次数。

    哈希表的特点:关键字在表中位置和它之间存在一种确定的关系。

哈希函数:一般情况下,需要在关键字与它在表中的存储位置之间建立一个函数关系,以f(key)作为关键字为key的记录在表中的位置,通常称这个函数f(key)为哈希函数。

hash : 翻译为“散列”,就是把任意长度的输入,通过散列算法,变成固定长度的输出,该输出就是散列值。

           这种转换是一种压缩映射,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,所以不可能从散列值来唯一的确定输入值。

           简单的说就是一种将任意长度的消息压缩到莫伊固定长度的消息摘要的函数。

hash冲突:(大师兄自己写的哦)就是根据key即经过一个函数f(key)得到的结果的作为地址去存放当前的key value键值对(这个是hashmap的存值方式),但是却发现算出来的地址上已经有人先来了。就是说这个地方要挤一挤啦。这就是所谓的hash冲突啦

二)哈希函数处理冲突的方法

1)开放定址法:

其中 m 为表的长度

对增量di有三种取法:

线性探测再散列   di = 1 , 2 , 3 , ... , m-1

平方探测再散列   di = 1 2 , -12 , 22 , -22 , 32 , -32 , ... , k2 ,  -k2

(大师兄备注:吗单,上面的平方探测再散列是加1的平方;减1的平方,加2的平方,减2的平方,加3的平方,减3的平方。。。加k的平方,减k的平方。卧擦,老师你能再坑点么?法科。要是你直接看这个平方探测再散列的di是怎么来的,不一定能看懂老师ppt的这个写法,是平方的意思。上面的红色字呢,相当于是老师的ppt,是对应上面的图片一起看的。)

随机探测再散列   di 是一组伪随机数列

例子:

我在上面的这个配图底部写的那个红色的12,我当时测试的时候,不知道这个12,也就是上面增量 di 的由来。不知道,限制知道了,那是1的2次方。。。。老师懒得或者说不会给数字打角标。

2)链地址法

3、4)再哈希、建立公共溢出区

3.再hash法,就是算hashcode的方法不止一个,一个要是算出来重复啦,再用另一个算法去算。反正很多,直到不重复为止咯。大师兄猜的

4.建立一个公共溢出区域,就是把冲突的都放在另一个地方,不在表里面。具体实现就 不知道啦,也是大师兄猜的。

总结一下的就是下面的四行字:
1.开放定址法(线性探测再散列,二次探测再散列,伪随机探测再散列)
2.再哈希法
3.链地址法(Java hashmap就是这么做的)
4.建立一个公共溢出区
<think>好的,我现在需要回答用户关于哈希冲突解决方法及Java HashMap的实现方式的问题。首先,我得回忆一下哈希冲突的基本概念。哈希冲突是指不同的键通过哈希函数计算后得到相同的哈希值,导致它们在哈希表中的位置冲突。解决这个问题的方法主要有几种,比如开放定址法、链地址法、再哈希法、建立公共溢出区等。 用户提到了Java中的HashMap,我需要确定它具体使用哪种方法。根据之前的引用资料,引用[2]和[3]都提到HashMap采用的是链地址法,也就是每个哈希桶(bucket)中使用链表来存储冲突的键值对。不过Java 8之后,当链表长度超过阈值(默认是8)时,链表会转换成红黑树,以提高查询效率。这一点需要特别说明,因为它是对传统链地址法的优化。 接下来,我需要整理解决哈希冲突的主要方法,并逐一解释。比如开放定址法包括线性探测、平方探测等,而链地址法则是将冲突的元素存储在同一个位置的链表中。再哈希法则使用多个哈希函数来减少冲突的可能性。公共溢出区则是一个独立区域存放冲突元素。 然后,结合HashMap的具体实现,详细说明链地址法如何工作。比如当发生冲突时,新的键值对会被添加到对应桶的链表末尾,而当链表过长时转换为红黑树。同时,可以提到负载因子和扩容机制,比如当元素数量超过容量乘以负载因子时,哈希表会扩容,减少冲突概率。 还需要对比其他方法,比如开放定址法和链地址法的优缺点。例如,开放定址法可能更容易产生聚集现象,而链地址法在负载因子较高时仍然能有效工作,但需要额外的空间存储指针。 另外,引用[4]中提到了HashMap和TreeMap的区别,虽然用户的问题不直接涉及TreeMap,但可以简要说明HashMap的哈希桶结构,而TreeMap使用红黑树实现有序存储,但这里的重点还是HashMap如何处理冲突。 在组织内容时,需要分点清晰,先介绍哈希冲突的概念,再列举解决方法,最后详细说明HashMap的具体实现,包括链地址法和树化策略。同时,确保使用正确的中文表达,避免技术术语错误。最后,生成相关问题时要围绕哈希冲突解决方法和HashMap的特性展开,比如性能优化、其他解决方法的比较等。 检查是否所有数学公式都正确使用$...$或$$...$$格式,但当前问题主要涉及数据结构,可能数学公式较少,但如果有涉及的地方需要注意格式。例如,当提到时间复杂度O(1)或O(logN)时,应使用$O(1)$和$O(\log N)$的格式。不过根据用户示例中的引用,可能直接使用原文中的格式,比如引用[4]中的O(logN),需要保持一致性。 最后,确保引用标识正确添加,比如链地址法的说明引用[2]和[3],树化策略引用[2],负载因子和扩容引用[1]和[2]。同时,回答结构要清晰,分步骤或分点说明,帮助用户逐步理解。</think>### 解决哈希冲突的方法及Java HashMap的实现方式 #### 一、哈希冲突的定义 哈希冲突指不同的键(Key)通过哈希函数计算后得到相同的哈希值,导致它们在哈希表中的存储位置冲突。例如,两个不同的键$k_1$和$k_2$满足$hash(k_1) = hash(k_2)$。 #### 二、解决哈希冲突的主要方法 1. **开放定址法** - 当冲突发生时,按特定规则(如线性探测、平方探测)寻找下一个空闲位置存储数据。 - 例如:线性探测公式为$hash'(k) = (hash(k) + i) \% m$,其中$i$为探测次数,$m$为表长。 2. **链地址法(Chaining)** - 每个哈希桶(Bucket)维护一个链表(或树结构),冲突的键值对直接添加到链表中[^2][^3]。 3. **再哈希法(Rehashing)** - 定义多个哈希函数,当冲突发生时,使用第二个哈希函数重新计算位置,直到找到空闲位置。 4. **公共溢出区法** - 将冲突的键值对统一存放到一个独立的溢出区域中。 --- #### 三、Java HashMap的实现方式 1. **链地址法为基础** - HashMap默认使用链表处理冲突,每个哈希桶对应一个单向链表,冲突的键值对按插入顺序链接[^3]。 - **示例**:若键$k_1$和$k_2$哈希值相同,则它们会被存储在同一个桶的链表中。 2. **树化优化(Java 8+)** - 当链表长度超过阈值(默认8)且哈希表容量≥64时,链表会转换为红黑树,将查询时间复杂度从$O(n)$优化为$O(\log n)$。 3. **负载因子与扩容机制** - 负载因子(默认0.75)控制哈希表扩容的时机。当元素数量超过`容量×负载因子`时,哈希表会扩容为原来的2倍,重新分配键值对以减少冲突概率[^1]。 4. **哈希函数优化** - HashMap通过扰动函数(如异或运算)混合键的哈希值的高位和低位,降低冲突概率。 - 公式示例: ```java static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); } ``` --- #### 四、与其他方法的对比 | 方法 | 优点 | 缺点 | |--------------|-------------------------------|-------------------------------| | 链地址法 | 高负载因子下仍高效 | 需额外空间存储指针 | | 开放定址法 | 无需额外数据结构 | 易产生聚集现象,删除操作复杂 | | 再哈希法 | 冲突概率低 | 计算成本高 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值