散列表总结
如果要转载,需要注明出处: http://blog.csdn.net/xiazdong
本文整理自《算法导论》第11章,由于本章有一些概率论知识,因此理解起来比较困难,但是一般只要记住结果即可。我在面试的时候也被问过:“请问哈希冲突的解决方法有哪些?”,这个问题的答案是:
第一种是链接技术,即用双向链表来链接哈希值相同的元素。这种方法能够有良好性能的前提是满足近似简单一致散列。第二种是开放寻址法,而开放寻址法有几种生成探查寻列的方法,如线性探查、二次探查、二次哈希(double hashing),其中二次哈希用的最多。开放寻址法有良好性能的前提是满足一致散列。(以上粗体字都是本文将要讲解的内容)
本文定义 n为实际要插入的关键字,m为槽的个数。
动态集合:支持插入、删除操作。静态集合:只支持查找操作。
字典操作:INSERT(T,x):将元素x插入T。DELETE(T,x):将元素x从T中删除。SEARCH(T,k):根据关键字k查找元素。
一、直接寻址法
定义:每个元素的关键字k就是数组的下标,比如一个元素的关键字为2,则T[2]就是元素存放的位置。被寻址的表称为直接寻址表。
前提:(1)关键字的全域U较小,不然机器创建一个很大很大的数组不太可能。(2)关键字都是自然数,不然不能直接寻址。(3)没有两个元素的关键字相同,即不能“碰撞”。
优点:简单,方便。并且没有冲突。缺点:(1)关键字的全域很大时,如2^64,则计算机不能创建这么大的数组。(2)如果关键字不是自然数,则不能直接寻址,比如关键字是string,则不能T[string]。(3)如果实际使用的关键字只有少数,但是全域很大,则会造成空间的浪费。比如全域为100000,实际使用了2个关键字。
二、散列表
定义:设计一个哈希函数h,如果关键字为k,则h(k)就是哈希表下标位置。
优点:需要的空间少。缺点:哈希冲突(书中称为“碰撞”)。
简单一致散列:如果任何一个关键字k,他映射到m个槽的任何一个的可能性都相同,并且k映射到哪个槽与其他关键字独立无关,则称为简单一致散列。装载因子:n/m,即一个槽的链表的平均长度。
全域散列:任何一个哈希函数,都存在一个键集,使得他们映射到同一个槽中(即诱导发生最坏情况),因此全域散列就有用武之地了,他是预先定义一个有限的散列函数集(就是多个散列函数),等到真正开始执行时,随机选择一个散列函数(一旦开始执行,散列函数就不能改变),这样的优点是对手不知道你要选那个散列函数,看到的只是random()。全域性质:从散列函数集中随机选择一个函数,两个关键字碰撞的概率至多1/m。
结论:(1)在散列函数集是全域的条件下,成功查找的期望时间至多1+n/m。(2)在散列函数集是全域的条件下,不成功查找的期望时间至多n/m。
证明结论: 
选取散列函数是一门很大的学问,但是有一些启发式的函数,如:1.除法散列法:h(k)= k mod m 注意点:
(1)m不能太小,m不能是2的幂次,m一定是质数。 (2)这个方法的优点是简单。
2.乘法散列法:h(k)=floor(m(kA mod 1)),这个方法一般比除法散列法好,因为乘法比除法快。 注意点:
(1)一般A=(根号5 - 1)/2 (2)一般m是2的幂次。如果不理解乘法散列法,可以做做算法导论11.3-4。
完全散列:适用于静态集合(只有search操作),利用两级哈希,使得最坏情况下查找只需要O(1)。原理如下图:
完全散列的要求:第二个散列函数必须是没有碰撞发生的。
三、解决碰撞的方法
有没有完全避免碰撞的方法呢?实际上是没有的,根据鸽巢原理,n只要大于m,一定至少有一个槽放了多于1个元素。
1、链接法
定义:设计一个哈希函数h,如果关键字为k,则h(k)就是哈希表下标位置。
优点:需要的空间少。缺点:哈希冲突(书中称为“碰撞”)。
简单一致散列:如果任何一个关键字k,他映射到m个槽的任何一个的可能性都相同,并且k映射到哪个槽与其他关键字独立无关,则称为简单一致散列。装载因子:n/m,即一个槽的链表的平均长度。
全域散列:任何一个哈希函数,都存在一个键集,使得他们映射到同一个槽中(即诱导发生最坏情况),因此全域散列就有用武之地了,他是预先定义一个有限的散列函数集(就是多个散列函数),等到真正开始执行时,随机选择一个散列函数(一旦开始执行,散列函数就不能改变),这样的优点是对手不知道你要选那个散列函数,看到的只是random()。全域性质:从散列函数集中随机选择一个函数,两个关键字碰撞的概率至多1/m。
结论:(1)在散列函数集是全域的条件下,成功查找的期望时间至多1+n/m。(2)在散列函数集是全域的条件下,不成功查找的期望时间至多n/m。
证明结论:

选取散列函数是一门很大的学问,但是有一些启发式的函数,如:1.除法散列法:h(k)= k mod m 注意点:
(1)m不能太小,m不能是2的幂次,m一定是质数。 (2)这个方法的优点是简单。
2.乘法散列法:h(k)=floor(m(kA mod 1)),这个方法一般比除法散列法好,因为乘法比除法快。 注意点:
(1)一般A=(根号5 - 1)/2 (2)一般m是2的幂次。如果不理解乘法散列法,可以做做算法导论11.3-4。
完全散列:适用于静态集合(只有search操作),利用两级哈希,使得最坏情况下查找只需要O(1)。原理如下图:

三、解决碰撞的方法
有没有完全避免碰撞的方法呢?实际上是没有的,根据鸽巢原理,n只要大于m,一定至少有一个槽放了多于1个元素。
1、链接法
用双向链表来链接哈希值相同的元素。这种方法能够有良好性能的前提是满足近似简单一致散列。
简单一致散列:如果任何一个关键字k,他映射到m个槽的任何一个的可能性都相同,并且k映射到哪个槽与其他关键字独立无关,则称为简单一致散列。
结论:
INSERT操作的最坏情况时间为O(1)
DELETE操作的最坏情况时间为O(1)
查找不成功的SEARCH操作的期望运行时间为O(1+n/m),最坏情况时间为O(n),当n=O(m)时,期望时间为O(1)
查找成功的SEARCH操作的期望运行时间为O(1+n/m),最坏情况时间为O(n),当n=O(m)时,期望时间为O(1)
证明:简单一致散列的假设下,查找不成功的期望时间为O(1+n/m)首先hash函数需要O(1),然后设Xk为T[k]的链表的期望长度,我们已知期望长度为n/m,查找不成功会遍历一个链表,因此查找不成功的期望时间为O(1+n/m)。
证明:简单一致散列的假设下,查找成功的期望时间为O(1+n/m)
