哈希表

转载自:http://blog.csdn.net/wangchaoxjtuse/article/details/6074292

1.哈希表
      根据给定的关键字,按照某种算法得出该关键字的哈希值,然后直接用哈希值读取所要查找的关键字记录。理论上哈希表是最快的查找表,他的查找时间复杂度理论上可以达到O(1),也就是直接读取。但是现实中往往哈希表的存储容量是有限的,而且不同的关键字有可能经过哈希算法得出相同的哈希值,因此有可能不同的关键字会映射到相同的哈希表项,这样就产生了冲突,这个现象叫做碰撞。
      这里就可以明确出哈希表中最关键的两个部分,第一个就是哈希函数(散列函数),第二就是解决冲突的办法。这两个因素综合决定了哈希表查找的时间复杂度。一个好的散列函数必须可以把所有关键字尽可能平均的映射到哈希表的任意一个表项,典型的散列方法有三种:除法散列法,乘法散列法和全域散列。
1)除法散列法
      h(k) = k mod m
      这里最关键的就是m的选取,一般我们定义装载因子α = n/m,m的选取就和α有关,也就是m = n/α,我们一般选择m为接近n/α,且不接近2的任何次幂的质数,切记m不能选择为2的任何次幂的数,而且m的选取和具体的应用环境有关系。
2)乘法散列法
      乘法散列法包含两个步骤:首先用关键字k乘上常数A,并抽取出结果的小数部分。然后用m乘以这个值并取结果的底。
      h(k) = floor(m*(k*A mod 1))
      乘法散列法的优点就是对m的选取没有限制,对A也没有什么限制,但是对于某些值来说效果要好一些,A的最佳选择和具体的待散列的数据特征有关。书上说Knuth认为 A ≈ (5开方 - 1) / 2 就是个比较理想的值。
3)全域散列法
      全域散列使用随机的办法来从一族已经设计的函数中选取一个来作为散列函数,关于这个散列法的分析和设计有滴滴复杂,等到真正用到的时候再说吧。
 
      说完了上面的散列函数,我们对哈希表的第一个问题解决的差不多了,接下来是第二个问题,如何解决碰撞的问题。一般有两种方法:链接法和开放寻址法。比较常用的是开放寻址法,重点说一下开放寻址法。
1)链地址
         链地址就是将映射到相同表项的关键字用链表穿起来,连成一串,查找的时候顺着这个链表去查找,显而易见的缺点就是用这种方法会让一些数据存储在哈希表之外的空间里,基本上这不是我们想要的结果。
 
2)开放寻址法
      开放寻址法不像链地址法,它没有链表,每一个数据项都是存储在哈希表的某个表项中,哈希表可能会被填满,但是其装填因子是绝对不会超过1的,开放寻址法的思想就是当发生碰撞的时候,会根据一个探查序列再一次进行散列,直到找到一个空的表项或者发现哈希表已经无法插入数据。
      对于开放寻址法的探查序列来说,它必须是一个0到m-1的排列,使得当哈希表逐渐被填满的时候每一个表项都可以用来填入关键字,查找的时候就可以根据相同的序列来找到关键字,这里有一个问题,当需要删除某个关键字的时候,不能单纯的将找到的关键字设置为无效,否则就不能再继续查找该关键字之后的关键字了,一般的做法是设置该关键字为已删除标记,但是这样做的话,查找的时间就不再依赖于装填因子了,也就是因为这样,在必须删除关键字的应用中,通常使用链地址来解决碰撞。
      计算探查序列的方法通常有三种:线性探查,二次探查和双重散列。
 
      线性探查
      线性探查的思想是按照顺序的方式依次探查下一个位置,直到最大,然后用求余的方法回到哈希表的头部,继续探查到第一次探查的前一个位置,例如第一次找到的位置为h(k),下一次就是h(k)+1,…,m-1,0,1,…,h(k)-1,第一次探查的位置决定了整个探查序列。这是一个很容易实现的方法,但是存在一个问题:一次群集。随着哈希表逐渐被填满越来越容易出现群集的现象,连续被占用的序列会越来越长,导致查找时间越来越大。
 
      二次探查
      二次探查可以减轻群集带来的效率降低,它在每一次冲突之后为探查号i加上一个偏移,得到新的探查号,这个偏移量是i的二次函数,这种方法和线性探查的思路类似,效果要好得多,但是仍然会产生群集,和线性探查一样第一次探查的位置决定了整个探查序列。
 
      双重散列
      双重散列是开放寻址法最好的一种探查方式,简单的说就是每次产生碰撞,都会根据关键字用另一个哈希函数重新算一个哈希值,这种散列方法的散列函数形式为:
      h(k,i) = (h1(k) + i*h2(k)) mod m
      由于两次都重新使用不同的哈希函数进行计算,使得这种方法和理想的一致散列性能比较接近了。
 
      哈希表这种数据结构在实际应用中是很广泛的,它看起来好像很简单,但是实际上,哈希表的应用技术远远要比我这里描述的复杂很多,这里只是对它的基本原理进行一个总结,在实际应用中遇到问题再讨论吧
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值