MIT算法导论公开课第七讲哈希表

     哈希表又称散列表,其定义是根据一个哈希函数将集合S中的关键字映射到一个表中,这个表就称为哈希表,而这种方法就称为Hashing。从作用上来讲,构建哈希表的目的是把搜索的时间复杂度降低到O(1),考虑到一个长度为n的序列,如果依次去比较进行搜索的话,时间复杂度是θ(n),或者对其先进行排序然后再搜索会更快一些,但这两种方法都不是最快的方法。


一、直接寻址表


      直接寻址表就是一个数组。每个数存在相应的下标内,当关键字的全域U比较小时,直接寻址是一种简单而有效的技术。比如,如果全域为U={0,1,…,m-1}。则可以使用长度为m的数组:

MIT算法导论公开课第七讲哈希表_第1张图片


二、哈希表


直接寻址技术的缺点很明显:如果全域U很大,则存储大小为|U|的数组是不切实际的,而且如果实际存储的关键字集合K相对于全域U来说很小的时候,会造成巨大的浪费。此时采用散列表。

在直接寻址方式下,具有关键字k的元素存放在索引为k的位置中,在哈希表中,该元素存放在h(k)中。h()就是一个散列函数。它将关键字的全域U映射到散列表T[0..M-1]的槽位上:

MIT算法导论公开课第七讲哈希表_第2张图片

这里会存在所谓“冲突”的问题:两个关键字可能被映射到同一个槽位中。由于全域|U|>M,所以冲突是无法避免的。一个好的哈希函数产生的键值应该尽可能的均匀,这样可以减少产生冲突的次数;但是无论是何种哈希函数也不可能完全解决冲突问题,所以还应该寻找解决冲突的方法。


哈希函数


除法散列法:散列函数为h(k) = k mod m。其中m为散列表的槽位数,使用除数散列法的时候,对于m的选择要慎重。比如m不应该是2的幂。否则如果m = ,则h(k)就是k的p个最低位数字(二进制)。除非已经知道关键字的最低p位数的排列是等可能的,否则在设计散列函数时,应该考虑关键字的所有位。一个不太接近2的整数幂的素数是m个一个比较好的选择。

乘法散列法:  假设所有的key都是整数,m=2^r ,计算机字长是w,那么构建h(k)=(A*k mod 2^w) rsh (w-r) 其中rsh是右移的意思,A的大小是2^(w-1)

 实际工作中需要根据不同的情况采用不同的哈希函数。通常,考虑的因素有:

 计算哈希函数所需的时间;

 关键字的长度;

 哈希表的大小;

 关键字的分布情况;

 记录的查找频率。


解决冲突的方法


1.链接法解决冲突。

解决冲突的比较简单的方法就是链接法。它是把散列到同一个槽位的所有元素都放在一个链表中,然后数组中存放指针指向这个链表,如下图:

MIT算法导论公开课第七讲哈希表_第3张图片

在最坏的情况下,也就是所有的h(k)都指向了同一个槽,那么哈希表实际上就是一个链表,在链表中查询一个值的时间复杂度是θ(n),在最好的情况下,没有发生碰撞那么时间为θ(1)。给定一个具有m个槽位,存储了n个元素的哈希表T,定义α=n/m为哈希表T的装载因子(即每个链表平均长度),一次成功的搜索平均用时θ(1+α/2)1表示计算H的时间,α/2表示在链表中所用的平均时间,所以如果n=O(m)那么α就是常数,在这个哈希表中搜索的时间就为θ(1),同时,考虑平均情况下的最坏情况的搜索,时间为θ(1+α)。由于插入操作首先需要调用CHAINED-HASH-SEARCH确认元素x的关键值未曾出现在表中,然后用O(1)时间将x插入到链表T[h(key[x])]中,所以期望的时间是O(1)。相仿地,删除操作对双向拉链表平均情形时间也是O(1),所以所有的字典操作可以在O(1)的平均时间内得到支持。

2.开放寻址法

   开放寻址法是另外一种处理冲突的方法。在该方法中,所有的元素都存放在哈希表中。当查找某个元素的时候,需要系统的检查所有的表项,直到找到所需的元素,或者最终查明该元素不在表中。因此,在开放寻址法中,哈希表有可能会被填满,因而装载因子α <=  1。

开放寻址法就是发生冲突时,采取某种探查方法,寻找下一个槽,如果下一个槽仍然有数,那么就继续探查,知道找到一个空的槽。常用的探查方法有:

1.线性探查。h(k,i) = (h’(k) + i) mod m, i = 0,1,…,m-1。给定一个关键字k,首先探查槽位h’(k),然后是h’(k) + 1,以此类推,直到最后的h’(k)-1。在线性探查中,初始探查位置决定了整个序列,所以有m种不同的探查序列。

       线性探查有个缺点,就是一次群集。当表中i,i+1,i+2位置上都已经填满时,下一个哈希地址为i,i+1,i+2,i+3的关键字记录都将竞争i+3的位置。随着连续被占用的槽位不断增加,平均查找时间也不断增加。

2.二次探查。 h(k,i) = (h’(k) + i + ) mod m,      i = 0,1,…,m-1 。二次探查即每次以平方的形式向后查找,知道找到空的槽。这种探查的效果要比线性探查好。但是,如果两个关键字的初始探查位置相同,那它们的探查序列也是相同的,这一性质会导致二次群集。类似于线性探查,二次探查也仅有m个不同的探查序列。
3.双重哈希探查。
4.伪随机序列探查。 双重散列是开放寻址法中的最好方法之一,不像线性和二次探查,双重探查的的探查序列以两种不同的方式依赖于关键字k。为了能使探查序列查找到整个表,值必须与m互素。有两种方法:

 m为2的幂,而 总产生奇数;

 取m为素数, 则总是产生比m小的正整数。

 双重探查法用到了 种探查序列。

在开放寻址哈希中,对于装载因子α,并假设是均匀散列,至多需要做1/(1-α)次探查。


参考:

《算法导论》

http://blog.csdn.net/gqtcgq/article/details/45289419

http://blog.csdn.net/persever/article/details/45773663




你可能感兴趣的:(Algorithm)