hash表如何形成,hash函数如何计算,什么是hash冲突 如何解决 ,Golang map ...

打印 上一主题 下一主题

主题 672|帖子 672|积分 2016

散列表

散列表(hash表):根据给定的关键字来计算出关键字在表中的地址的数据结构。也就是说,散列表创建了关键字和
存储地址之间的一种直接映射关系。
问题:如何创建映射管血

散列函数:一个把查找表中的关键字映射成该关键字对应的地址的函数,记为Hash(key)=Addr.
hash冲突

散列函数可能会把两个或两个以上的不同关键字映射到同一地址,称这种情况为“冲突
这些发生碰撞的不同关键字称为同义词。

构造散列函数的条件:



  • 散列函数的定义域必须包含全部需要存储的关键字,而值域的范围则依赖于散列表的大小或地址范围。
  • 散列函数计算出来的地址应该能等概率、匀称地分布在整个地址空间,从而淘汰冲突的发生。
  • 散列函数应尽量简单,可以或许在较短的时间内就计算出任一关键字对应的散列地址。
1.常用的Hash函数的构造方法:



  • 直接定址法:直接取关键字的某个线性函数值为散列地址,散列函数为H(key)=axkey+b。式中,a和b是
    常数。这种方法计算最简单,而且不会产生冲突
  • 除留余数法:假定散列表表长为m,取一个不大于m但最靠近或等于m的质数p,利用以下公式把关键字
    转换成散列地址。散列函数为H(kev)=key %p
    除留余数法的关键是选好p,使得每一个关键字通过该函数转换后等概率地映射到散列空间上的任一地址
    从而尽可能淘汰冲突的可能性
  • 数字分析法
    设关键字是r进制数(如十进制数),而r个数码在各位上出现的频率不一定雷同,
    可能在某些位上分布匀称些
    每种数码出现的机会均等;而在某些位上分布不匀称,只有某几种数码经常出现,
    则应选取数码分布较为匀称
    的若干位作为散列地址。这种方法得当于已知的关键字集合

手机号后四位分布比较均与 如果key为手机号那么取手机号后四位为散列值
4.平方取中法
顾名思义,取关键字的平方值的中心几位作为散列地址。详细取多少位要看现实情况而定。这种方法得
到的散列地址与关键字的每一位都有关系,使得散列地址分布比较匀称。
Eg
12342=1522756 取中心三位227作为散列地址
23452=5499025 取中心三位990作为散列地址
5.折叠法
将关键字分割成位数雷同的几部分 (最后一部分的位数可以短一些),然后取这几部分的叠加和作为
散列地址,这种方法称为折叠法。关键字位数许多,而且关键字中每一位上数字分布大致匀称时,可
以采用折香法得到散列地址。
Eg:关键字为1234567890散列表表长为3位可以将关键字分为四组每组3位
123456|7890然后这四组叠加求和123+456+789+0=1368 然后取后3位就能得到散列地址为368
2.常用的Hash函数的冲突处理办法

1.开放定址法

​ :将产生冲突的Hash地址作为自变量,通过某种冲突解决函数得到一个新的空闲的Hash地址。
  1.                 1)   线性探测法: 冲突发生时,顺序查看表中下一个单元 (当探测到表尾地址m-1时,探测地址是表首地址0),直到找出一个空闲单元 (当表未填满时一定能找到一个空闲单元)或查遍全表。
复制代码

如上图,11 、19、27经过运算后的映射下标都是3,由于3被最开始的11占用,那19就不得不后移占住背面空着的下表,这样就会产生连锁反应导致原来hash值是4的key也要后移,这样查找起来就非常贫苦
好比我找27,先计算得出27的hash值是3,但发下3下面存的11,只能往后找4 – 19不是,继续遍历到5 – 17 找到了
2)平方探测法:设发生冲突的地址为d,平方探测法得到的新的地址序列为d+12,d-12,d+22,d-22
平方探测法是一种较好的处理冲突的方法,可以避免出现“堆积”问题,它的缺点是不能探测到散列
表上的所有单元,但至少能探测到一半单元。

3)再散列法:又称为双散列法。需要利用两个散列函数,当通过第一个散列函数H(Key)得到的地址发
生冲突时,则利用第二个散列函数Hash2(Kev)计算该关键字的地址增量。

4)伪随机序列法:当发生地址冲突时,地址增量为伪随机数序列,称为伪随机序列法。
2.拉链法:

对于不同的关键字可能会通过散列函数映射到同一地址,为了避免非同义词
发生冲突,可以把所有的同义词存储在一个线性链表中,这个线性链表由其散列地址唯
标识。拉链法实用于经常进行插入和删除的情况
3.散列表的查找过程

类似于构造散列表,给定一个关键字Key。
先根据散列函数计算出其散列地址。然后查抄散列地址位置有没有关键字.
1)如果没有,表明该关键字不存在,返回查找失败。
2)如果有,则查抄该纪录是否等于关键字。


  • 如果等于关键字,返回查找成功。
  • 如果不等于,则按照给定的冲突处理办法来计算下一个散列地址,再用该地址去执行上述过程。
4.散列表的查找性能

性能和装填因子有关
什么是装填因子?


Golang中map的hash值是如何计算如何存的?

在日常编程中,map的底层也是hash表,但map 的 key 键不仅仅是数字,还可能是字符串。由于我们之前讨论的哈希函数主要是针对数字进行计算的,因此,当 key 为字符串时,首先需要将字符串转化为整数,然后再经过哈希函数的运算产生哈希值。
在 Go 中,map 的哈希值会通过取模运算(或其他方式)转换成哈希表中的一个详细位置,这个位置称为映射地址。映射地址通常对应于一个桶,桶是一个存储多个键值对的容器。每个桶可以存储多个键值对,以处理哈希冲突。桶的数量通常与哈希表的容量有关。
溢出桶:如果主桶已满,则会创建一个溢出桶,将多余的键值对存储在溢出桶中。如果溢出桶也满了,就会继续创建新的溢出桶,形成一个链表结构。

ps:个人认为桶和线性链表及其相似
go map的扩容机制

扩容的机会装载因⼦超过⼀定的阈值或者使⽤了太多的溢出桶时。
扩容的规则:


  • 等量扩容 使⽤溢出桶太多的时候会进⾏等量扩容。申请和原来等量的内容,将原来的数据重新整理
后,写⼊到新的内存中。可以简单的认为是⼀次内存整理,⽬的是提⾼查询效率。

  • 增量扩容 分成两步: 第⼀步进⼊扩容状态,先申请⼀块新的内存,翻倍增长桶的数量,此时
buckets指向新分配的桶,oldbuckets指向原来的桶。 第⼆步,重新计算⽼的桶中的哈希值在新
的桶内的位置(取模或者位操纵),将旧数据⽤渐进式的⽅式拷⻉到新的桶中。
渐进式迁移分两块,⼀⽅⾯会从第⼀个桶开始,顺序迁移每⼀个桶,如果下⼀个桶已经迁移,则跳
过。另⼀⽅⾯,当我们操纵某⼀个桶的元素时,会迁移两个桶,进⽽包管经过⼀些操纵后⼀定可以或许完
成迁移。
访问迁移的map时会放生什么?
当我们访问⼀个正在迁移的Map时,如果存在oldbuckets,那么直接去中oldbuckets寻找数据。当
我们遍历⼀个正在迁移的Map时,新的和旧的就会遍历,如果⼀个旧的的桶已经迁移⾛了,那么就直
接跳过,反正不在旧的就在新的⾥。Map遍历本⾝就是⽆序的。
定可以或许完
成迁移。
访问迁移的map时会放生什么?
当我们访问⼀个正在迁移的Map时,如果存在oldbuckets,那么直接去中oldbuckets寻找数据。当
我们遍历⼀个正在迁移的Map时,新的和旧的就会遍历,如果⼀个旧的的桶已经迁移⾛了,那么就直
接跳过,反正不在旧的就在新的⾥。Map遍历本⾝就是⽆序的。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

商道如狼道

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表