计算查找---哈希法

哈希


搜索时有静态环境和动态环境。
静态环境下:在执行插入和删除等操作的前后搜索结构不会发生改变;
动态环境下:为保持较高的搜索效率,搜索结构在执行插入和删除等操作的前后将自动调整,结构可能会发生变化

静态查找:只在数据元素集合中查找是否存在关键字等于某个给定关键字的数据元素。
动态查找:除包括静态查找的要求外,还包括在查找的过程中同时插入数据元素集合中不存在的元素,或者从数据元素集合中删除已存在的某个数据元素的要求。即如果在某个数据元素集合中进行了动态查找,则该数据元素集合可能会被改变。

线性表,二叉搜索树、AVL树、红黑树和B树中,元素在存储结构中的位置与元素的关键码之间不存在直接的对应关系。在数据结构中搜索一个元素需要进行一系列的关键码比较。搜索的效率取决于搜索过程中比较的次数。

理想的搜索方法是可以不经过任何比较,一次直接从表中得到要搜索的元素

哈希基本思想

在元素的关键码k和存储位置p建立一个对应关系H,使得p = H(k) , H称为散列函数(哈希函数)
在插入时,根据待插入元素的关键码,用函数计算出该元素的存储位置并按此位置进行存放。
在搜索时,对元素的关键码进行同样的计算,把求得的函数值当做元素的存储位置,在结构中按此位置取元素比较,若关键码相等,则搜索成功。该方式即散列方法(Hash Method),在散列方法中使用的转换函数叫着散列函数(Hash function),构造出来的结构叫散列表(Hash Table)。

用该方法进行搜索不必进行多次关键码的比较,因此搜索的速度比较快。

哈希函数

构造哈希函数需要注意以下几点:
1、哈希函数的定义域必须包括需要存储的全部关键码,而如果散列表允许有m个地址
时,其值域必须在0到m-1之间
2、哈希函数计算出来的地址能均匀分布在整个空间中, 目的是减少冲突。
3、哈希函数应该比较简单

常见的哈希函数

直接定址法
取关键字的某个线性函数为散列地址:Hash(Key)= A*Key + B。
优点:简单、均匀
缺点:需要事先知道关键字的分布情况
适合查找比较小且连续的情况。
除留余数法
设散列表中允许的地址数为m,取一个不大于m,但最接近或者等于m的质数p作为除
数,按照哈希函数:Hash(key) = key % p p<=m,将关键码转换成哈希地址。
平方取中法
假设关键字是1234,那么它的平方就是1522756,再抽取中间的3位就是227作为散列地
址;再比如关键字是4321,那么它的平方就是18671041,抽取中间的3位就可以是671
或者710用作散列地址。平方取中法比较适合:不知道关键字的分布,而位数又不是
很大的情况。
折叠法
折叠法是将关键字从左到右分割成位数相等的几部分(注意:最后一部分位数不够时可
以短些),然后将这几部分叠加求和,并按散列表表长,取后几位作为散列地址。比
如:关键字是9876543210,散列表表长为三位,我们将它分成四组987|654|321|0|,
然后将它们叠加求和987+654+321+0=1962,再求后3位得到散列地址为962。有时可能
这还不能够保证分布均匀,不妨从一段向另一端来回折叠后对齐相加。比如将987和
321反转,再与654和0相加,编程789+654+123+0=1566,此时的散列地址为566。折叠
法适合事先不需要知道关键字的分布,适合关键字位数比较多的情况。
随机数法
选择一个随机函数,取关键字的随机函数值为它的哈希地址,即H(key) = random(key),其中
random为随机数函数,通常应用于关键字长度不等时采用此法。
数字分析法
设有n个d位数,每一位可能有r种不同的符号,这r种不同的符号在各位上出现的频率
不一定相同,可能在某些位上分布比较均匀,每种符号出现的机会均等,在某些位上
分布不均匀只有某几种符号经常出现。可根据散列表的大小,选择其中各种符号分布
均匀的若干位作为散列地址。比如:
假设要存储某家公司员工登记表,如果用手机号作为关键字,那么极有可能前7位都是
相同的,那么我们可以选择后面的四位作为散列地址,如果这样的抽取工作还容易出现
冲突,还可以对抽取出来的数字进行反转(如1234改成4321)、右环位移(如1234改成
4123)、左环移位、前两数与后两数叠加(如1234改成12+34=46)等方法,总之:为了提
供一个散列函数,能够合理的将关键字分配到散列表的个位置。数字分析法通常适合
处理关键字位数比较大的情况,如果事先知道关键字的分布且关键字的若干位分布
较均匀的情况。
哈希冲突

两个关键字key1 != key2,经过哈希函数却有HH(key1) == H(key2),这样就产生了哈希冲突。

解决哈希冲突的方法
  • 闭散列
    闭散列也叫开地址法。设散列表的编址为0到m-1,当添加关键码key时通过散列函数
    hash(key)计算key的存放位置,但在存放时发现这个桶已经被另一个key,x占据了,即发生哈希冲突。如果表未被装满,表示在给定的范围内必然还有空位置,则可以把key存放到表中“下一
    个”空位中。
    简而言之:一旦发生冲突,就去寻找下一个空的散列表地址,只要散列表足够大,空的散列地址
    总能找到。

    • 线性探查
      设给出一组元素,它们的关键码为:37,25,14,36,49,68,57,11,散列表为HT[12],表的大小
      m =12,假设采用Hash(x) = x % p (p = 11是最接近m的质数),就有:
      Hash(37) = 4 Hash(25) = 3 Hash(14) = 3 Hash(36) = 3
      Hash(49) = 5 Hash(68) = 2 Hash(57) = 2 Hash(11) = 0
      这里写图片描述
      采用线性探查法处理冲突
      添加元素时,使用散列函数确定元素的插入位置,如果此空间有值:
      1、该值是所要插入元素的关键码,不进行插入
      2、产生冲突,依次查看其后的下一个桶,如果发现空位置插入新元素
      线性探查法容易产生数据“堆积”,即不同探查序列的关键码占据了可利用的空位置,使得寻找某关键码的位置需要许多次比较,导致搜索时间增加。
      在闭散列的情形下不能随便物理删除表中已有的元素。因为若删除元素会影响其他元素的搜索。
      这里写图片描述
      这里写图片描述
    • 二次探查
      使用二次探查法,在表中寻找“下一个”空位置的公式为:
      Hi = (H0 + i^2)%m, Hi = (H0 - i^2)%m, i = 1,2,3…,(m-1)/2
      H0是通过散列函数Hash(x)对元素的关键码x进行计算得到的位置,m是表的大小。
      假设数组的关键码为37,25,14,36,49,68,57,11,假设取m=19,这样可设定为HT[19],采用散列函
      数Hash(x) = x % 19
      Hash(37)=18 Hash(25)=6 Hash(14)=14 Hash(36)=17
      Hash(49)=11 Hash(68)=11 Hash(57)=0 Hash(11)=11
      这里写图片描述
      研究表明当表的长度为质数且表装载因子a不超过0.5时,新的表项一定能够插入,而且任何一个
      位置都不会被探查两次。因此只要表中有一半的空的,就不会有表满的问题。在搜索时可以不考虑
      表装满的情况,但在插入时必须确保表的装载因子a不超过0.5;如果超出必须考虑增容。

    • 双散列法
      使用双散列方法,需要两个散列函数。第一个散列函数Hash()按关键码key计算其所在的位置H0 =Hash(key)。一旦发生冲突,利用第二个散列函数ReHash()计算该key到达“下一个”桶的位移,它的取值与key的值有关,要求它的取值应该是小于地址空间大小TableSize,且与TableSize互质的正整数。
      若设表的长度为m = TableSize,则在表中寻找“下一个”桶的公式为:
      j = H0 = Hash(key),p = ReHash(key); j = (j+p)%m; p是小于m且与m互质的整数。

  • 开散列法
    开散列法又叫链地址法(开链法)。
    开散列法首先对关键码集合用散列函数计算散列地址,具有相同地址的关键码归于同一子集合,每一个子集合称为一个桶,各个桶中的元素通过一个单链表链接起来,各链表的头结点组成一个向量,因此,向量的元素个数与可能的桶数一致。
    设元素的关键码为37,25,14,36,49,68,57,11,散列表为HT[12],表的大小为12,散列函数为Hash(x)= x % 11
    Hash(37)=4 Hash(25)=3 Hash(14)=3 Hash(36)=3
    Hash(49)=5 Hash(68)=2 Hash(57)=2 Hash(11)=0
    采用开散列法处理冲突:
    这里写图片描述
    通常,每个桶中的同义词子表都很短,设有n个关键码通过某一个散列函数,存放到散列表中的m个桶中,那么每一个桶中的同义词子表的平均长度为n/m。这样以搜索平均长度为n/m的同义词子表代替了搜索长度为n的顺序表,搜索效率快的多。

实现哈希表的C++代码:
C++模拟实现哈希

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值