• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

哈希表(hash_table)的原理

武飞扬头像
物随心转
帮助1

一、hash_table的介绍

hash_table可提供对任何键值对的存取和删除操作。由于操作对象是键值对,所以hash table也可被视为一种字典结构(dictionary)。这种结构的用意在于提供常数时间的基本操作,就像stack或queue那样。乍听之下这几乎是不可能的任务,因为约束制条件如此之少,而随着元素个数增加,搜寻操作必定耗费更多时间。

二、问题

举个例子,这里有一些元素,都是16-bits且不带正负号的整数,范围0-65535,如何存储这些整数,并快速的查找呢?
我们用一个array就可以满足上述期望。 首先配置一个array A,它拥有65536个元素,索引号码0-65535,初值全部为0,如下图5-21,每一个元素值代表相应元素的出现次数。如果插入元素i,我们就执行A[i] ,如果删除元素i,我们就执行A[i]--,如果搜寻元素i,我们就检查A[i]是否为0。以上的每一个操作都是常数时间.这种解法的额外负担的是array的空间和初始化时间。

学新通

三、分析

这个解法存在两个问题:

第一,如果元素是32-bits,而非16-bits,我们所准备的array A的大小就必须是2的32次方=4GB,这就大得不切实际了。

第二,如果元素型态是字符串而非整数,将无法被拿来作为array的索引。

第二个问题不难解决。就像数值1234是由阿拉伯数字1,2,3,4构成一样,字符串"jhou”是由字符·j',‘j',h','o,'u'构成。那么,既然数值1234是1*103 2*102 3*101 4*10°,我们也可以把字符编码,每个字符以一个7-bits数值来表示(也就是ASCII编码),从而将字符串"jjhou·表现为:
'j*128 ·j1*1283 "h'*1282 "0'*1281 ·u'*1280。于是先前的array实现法就可适用于“元素型别为字符串”的情况了。但这并不实用,因为这会产生出非常巨大的数值."jhou·的案引值将是:
106*1284 106*1283 104*1282 111*1282 117*128°=28678174709。这太不切合实际了。更长的字符串会导致更大的索引值!

这就回归到第一个问题:array的大小。如何避免使用一个大得荒谬的array呢?办法之一就是使用某种映射函数,将大数映射为小数。负责将某一元素映射为一个“大小可接受之索引”,这样的函数称为hash function(散列函数)

例如,假设X是任意整数,Tablesize是array大小,则X%Tablesize会得到一个整数,范围在0-Tablesize-1之间,正好作为表格(也就是array)的索引,使用散列函数会带来一个问题:可能有不同的元素被映射到相同的位置(即有相同的索引)。这无法避免,因为元素个数大于array容量,这便是所谓的“碰撞(collision)”问题。解决碰撞问题的方法有许多种,包括线性探测(linear probing)、二次探测(quadratic probing)、开链(separate chaining)…等做法。

四、线性探测(linear probing)

当hash function计算出某个元素的插人位置,而该位置上的空间已不再可用时,我们应该怎么办?

最简单的办法依次向后探测,直到寻找到下一个空位置为止。(如果到达尾端,就绕到头部继续寻找)。只要表格(亦即array)足够大,总是能够找到一个安身立命的空间,但是要花多少时间就很难说了,进行元素搜寻操作时,道理也相同,如果hash function计算出来的位置上的元素值与我们的搜寻目标不符,就循序往下一个一个地寻找,直到找到吻合者,或直到遇上空格元素。

学新通

 五、二次探测(quadratic probing)

二次探测法是指采用前后跳跃方式探测的方法,发生冲突时,向后 1 位探测,向前 1 位探测,向后 4 位探测,向前 4 位探测......以跳跃式探测,避免堆积。

二次探测的增量序列为 d=1,-1,4,-4,9,-9,16,-16,,,

若当前扫描的元素的地址已经有元素了,那么,当前元素就保存在该地址的后移偏量。

举个例子:有数组{47, 7, 29, 11, 16, 92, 22, 8, 3} ,接下来我们将所有元素对11取余,如下图所示。

学新通

 首先创建一个散列表,现在根据取余的值将元素放入散列表,如下图所示。

学新通

 其中47,7,11,16,92这些元素是根据取余的值直接放入散列表的。而29取余的值为7,7的位置上已经有元素了,那么我们放在7 1^2的位置上。3取余的值是3,3的位置上也已经有元素了,那么我们看3 1^2上也有元素,再看3-1^2的位置上没有元素,那么我们现在就放在这里。那么其他元素也是一样的道理。

 六、开链(separate chaining)

另一种与二次探测法分庭抗礼的,是所谓的开链(separate chaining)法。这种做法是在每一个表格元素中维护一个list。散列函数为我们分配某一个list,然后我们在那个list身上执行元素的插入,搜寻、删除等操作.虽然针对list而进行的搜寻只能是一种线性操作,但如果list够短,速度还是够快的

学新通 

参考:

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhfifbbj
系列文章
更多 icon
同类精品
更多 icon
继续加载