数据结构 - 解决Hash冲突(碰撞)四种常用方法

一、预备知识

1、非哈希表的特点:关键字在表中的位置和它之间不存在一个确定的关系,查找的过程为给定值一次和各个关键字进行比较,查找的效率取决于和给定值进行比较的次数。
2、哈希表的特点:关键字在表中位置和它之间存在一种确定的关系。
3、哈希函数:一般情况下,需要在关键字与它在表中的存储位置之间建立一个函数关系,以f(key)作为关键字为key的记录在表中的位置,通常称这个函数f(key)为哈希函数。
4、hash:翻译为“散列”,把任意长度的输入通过hash算法变换成固定长度的输出,这个输出就是Hash值。这种转换是一种压缩映射,哈希值的空间远小于输入的空间,所以可能会发生“哈希碰撞”,即两个不同的输入,产生了同一个输出,所以不可能从散列值来唯一的确定输入值。简单来说就是,一种将任意长度的消息压缩到莫伊固定长度的消息摘要的函数,Hash算法常用于消息摘要的场景 MD5、SHA都属于Hash算法的实现。

5、hash冲突:根据key即经过一个函数f(key)得到的结果的作为地址去存放当前的key value键值对(这个是hashmap的存值方式),但发现与其他的对象计算值一样。

例如:当我们对某个元素进行哈希运算,得到一个存储地址,要插入时发现已被其他元素占用。

二、解决方案

1、 开放定址法(再散列法):

  当关键字key的哈希地址p出现冲突时,以p为基础,产生另一个哈希地址p1,如果p1仍然冲突,再以p为基础,产生另一个哈希地址,直到找出一个不冲突的哈希地址pi ,将相应元素存入其中。

  一旦发生了冲突,就去寻找下一个空的散列地址,只要散列表足够大,空的散列地址总能找到,并将记录存入公式为:
  fi(key) = (f(key)+di) MOD m (di=1,2,3,……,m-1) 
  ※ 做法是:当冲突发生时,使用某种探测技术在散列表中形成一个探测序列。沿此序列逐个单元地查找,直到找到给定的关键字,或者碰到一个开放的地址(即该地址单元为空)为止(若要插入,在探查到开放的地址,则可将待插入的新结点存人该地址单元)。查找时探测到开放的地址则表明表中无待查的关键字,即查找失败。 

  比如说,我们的关键字集合为{12,67,56,16,25,37,22,29,15,47,48,34},表长为12。

  我们用散列函数f(key) = key mod l2 
  当计算前S个数{12,67,56,16,25}时,都是没有冲突的散列地址,直接存入: 

  这里写图片描述 
  计算key = 37时,发现f(37) = 1,此时就与25所在的位置冲突。 
  我们应用上面的公式f(37) = (f(37)+1) mod 12 = 2。

  于是将37存入下标为2的位置: 
  这里写图片描述

2、再哈希法(双哈希法): 

  在发生冲突时,再用第二个,第三个...哈希函数算出哈希值,直到算出的哈希值不同为止。虽然不易发生聚集,但增加了计算时间。

3、链地址法(拉链法): 

  把同一个散列槽(数组的每一个槽)中的所有元素放到一个链表中。

  每个哈希表节点都有一个next指针,多个哈希表节点可以用next指针构成一个单向链表,被分配到同一个索引上的多个节点可以用这个单向链表连接起来,如:
  键值对k2, v2与键值对k1, v1通过计算后的索引值都为2,此时产生冲突,但是可以通道next指针将k2, k1所在的节点连接起来,这样就解决了哈希的冲突问题。  
  这里写图片描述

4、建立公共溢出区:

  将哈希表分为基本表和溢出表两部分,凡是和基本表发生冲突的元素,一律填入溢出表。建立一个公共溢出区域,把冲突的都放在另一个地方,不在表里面。

posted @   李若盛开  阅读(1635)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
点击右上角即可分享
微信分享提示