Hash Table

1.Hash functions

  • 直接定址法:h(k)=ak+b.
  • 数字分析法
  • 平方取中法:去关键字平方后的中间几位为hash address.
  • 折叠法:将关键字分割成位数相同的几部分(最后一部分位数可以不同),然后取这几部分的叠加(去掉进位)和作为hash address.
  • 除留余数法:h(k)=k mod m, m<=Table.length.一般情况下,取m为质数或不包含小于20的质因数的合数。
  • 随机数法:h(k)=random(k), 当关键字长度不同时采用此法较恰当。
  • 乘法散列法(multiplication method):h(k)= [m(kA mod 1)], m=2p, A≈(√5 - 1)/2 (best).
  • 全域散列法(universal hashing):从一组精心设计的functions中随机的选择hash function.

2.解决冲突的方法

  • 再Hash法:关键字产生冲突时,再用其它不同的hash function再计算一次地址。
  • 链接法:将所有关键字为同义词的元素放在同一个线性链表中。
  • 开放寻址法:
    • 线性探测:h(k,i)=(h'(k)+i) mod m, i=0,1,...,m-1.存在primary clusting问题。(群集:在函数地址的表中,散列函数的结果不均匀地占据表的单元,形成区块。散列到区块中的任何关键字需要查找多次试选单元才能插入表中,解决冲突,造成时间浪费。对于开放寻址法,聚集会造成性能的灾难性损失,是必须避免的。
    • 二次探测:h(k,i)=(h'(k)+c*i+d*i2) mod m, i=0,1,...,m-1.存在secondary clusting问题,程度较轻。
    • 双重散列(double hashing):h(k,i)=(h1(k)+i*h2(k)) mod m, i=0,1,...,m-1.为了能查找整个散列表,值h2(k)必须与m互素。
      • 方案一:m取2的幂,并设计一个总产生奇数h2。
      • 方案二:m为素数,并设计一个总是返回较m小的正整数的函数h2。
  • 建立一个公共溢出区:一旦发生冲突,都填入溢出表。

3.完全散列(perfect hashing)

采用两级的散列方法来设计散列方案,每一级上都采用全域散列。适用于关键字集合是静态的情况,即关键字一旦存入表中,关键字集合就不再改变。如程序设计语言中保留字的集合,CD—ROM上的文件名的集合。

4.采用开放寻址法的散列表中的删除操作

删除关键字时,不能仅仅将该位置置空(NIL),比如两个关键字的hash值相同时,那么第二个关键字就要往后移,这种情况下删除第一个关键字将会导致第二个关键字无法被检索到。一个解决方法是用特定的值DELETED来标记该位置。在必须删除关键字的应用中,更常见的做法是采用链接法来解决冲突。(python中字典是通过散列表实现,用开放寻址法解决冲突,搜索的时间复杂度为T(n)=O(1);c++的STL通过红黑树实现,搜索的时间复杂度T(n)=O(lgn),SGI的STL通过链接法解决冲突。)

5.时间复杂度

搜索、插入、删除的时间复杂度:T(n)=O(1).

6.查找效率、载荷因子

6.1查找效率

对散列表查找效率的量度,用平均查找长度来衡量。

查找过程中,关键码的比较次数,取决于产生冲突的多少,产生的冲突少,查找效率就高,产生的冲突多,查找效率就低。影响产生冲突多少有以下三个因素:

  1. 散列函数是否均匀;
  2. 处理冲突的方法;
  3. 散列表的载荷因子(load factor)。

6.2载荷因子

散列表的载荷因子定义为:alpha = 填入表中的元素个数 / 散列表的长度

alpha是散列表装满程度的标志因子。由于表长是定值,alpha与“填入表中的元素个数”成正比,所以,alpha越大,表明填入表中的元素越多,产生冲突的可能性就越大;反之,alpha越小,标明填入表中的元素越少,产生冲突的可能性就越小。实际上,散列表的平均查找长度是载荷因子alpha的函数,只是不同处理冲突的方法有不同的函数。

对于开放寻址法,荷载因子是特别重要因素,应严格限制在0.7-0.8以下。超过0.8,查表时的CPU缓存不命中(cache missing)按照指数曲线上升。因此,一些采用开放寻址法的hash库,如Java的系统库限制了荷载因子为0.75,超过此值将resize散列表。

原文地址:https://www.cnblogs.com/bukekangli/p/4275946.html