散列表:
(零)步骤:
 (1)用给定的哈希函数构造哈希表。
 (2)根据选择的冲突处理方法解决地址冲突。
 (3)在哈希表的基础上执行哈希查找,插入,删除。
(一)散列函数:
 把线性表中的关键字映射为关键字对应的地址的函数,记为Hash(key)=addr.
 (这里的地址可以是数组下标,索引,或内存地址等)

 1)散列函数定义域和值域:
 定义域:包含所有需要存储的关键字。
 值域:依赖于散列表的大小或地址范围。

(二)冲突与同义词:
 散列函数把两个或多个不同的关键字映射到同一个地址,成为冲突;
 发生碰撞的不同关键字,称为同义词;

(三)散列表:
 散列表是根据关键字而直接访问的数据结构。即,散列表建立了关键字到地址之间的一种直接映射关系。
 理想情况下,对散列表进行查找的时间复杂度为O(1),与表中的元素个数无关。


(四)构造散列函数:

 (1)直接定址法:
 直接取关键字的某个线性函数值为散列地址,散列函数为H(key)=a*key+b;
 特点:不会发生冲突,适合关键字的 分布比较连续的 情况,若关键字分布不连续,空位较多。

 (2)留余法:
 假定散列表的长为m,取一个不大于m但接近于m的质数p,散列函数为:
 H(key)=key%p;

 (3)平方取中法:
 取关键字的平方的中间几位作为散列地址;

 (4)叠加法:
 将关键字分为位数相同的几个部分(最后一部分数的位数可能短一些),然后取这几部分的叠加和作为散列地址。
 适用于:关键字的位数较大。
 如:关键字为92365478,散列表的长度为1000,则划分结果为每段3位,分为923,654,78,散列地址为:(923+654+78)%(小于1000的最近质数);

(五)处理冲突的方法:

 (1)开放定址法:
  1)定义:
  Hi=(H(key)+di)%m;
  i=1,2,3...,k(k<=m-1);m表示散列表的表长;di为增量序列。Hi表示发生冲突后第i次得到的散列地址。
  2)线性探测法:
  当di=1,2,....,m-1;称为线性探测法。
  特点:
  冲突发生时,顺序查看下一个单元(当探测到表尾地址m-1时,下一个探测地址为表头地址0),直到找到一个空闲单元(当表没有满则一定可以找到一个空闲单元)或查遍全表。
  缺点:可能使得第i个散列地址的同义词存入到第i+1个散列地址,而本应存入第i+1个地址的元素就争夺第i+2个散列地址的元素的地址....;大大降低了查找效率。

  3)平方探测法:当di=1^2,-1^2,2^2,-2^2,3^2,-3^2,...,k^2,-k^2;
  其中m必须是一个可以表示为4k+3的质数。
  缺点:不能保证探测到散列表中的所有单元。

  4)再散列法:

  di=Hash2(key),使用两个散列函数,当通过第一个散列函数H(key)得到的地址冲突时,则利用Hash2(key)作为增量。

  5)伪随机序列法:
  di=伪随机序列;

  6)注意:在开放定址法中,不可以随便物理删除散列表中的已有的元素,因为若删除元素将截断其他具有相同散列地址 的元素的查找地址。
  所以若想删除一个元素时,给它做一个删除标记,进行逻辑删除。
  缺点:在执行多次删除之后,表面上散列表很满,实际上有许多位置没有利用,所以需要定期维护散列表,把要删除标记的元素物理删除。


 (2)拉链法:
  1)定义:

  将所有关键字为同义词的结点链接在同一个单链表中。若选定的散列表长度为m,则可将散列表定义为一个由m个头指针组成的指针数组T[0..m-1]。凡是散列地址为i的结点,均插入到以T[i]为头指针的单链表中。T中各分量的初值均应为空指针。
  2)范例:

hash表_legend_散列函数

  3)拉链法的优点:
  (1)拉链法处理冲突简单,且无堆积现象,即非同义词决不会发生冲突,因此平均查找长度较短;
    (2)由于拉链法中各链表上的结点空间是动态申请的,故它更适合于造表前无法确  定表长的情况;
    (3)开放定址法为减少冲突,要求装填因子α较小,故当结点规模较大时会浪费很多 空间。而拉链法中可取α≥1,且结点较大时,拉链法中增加的指针域可忽略不计,因此节 省空间;
    (4)在用拉链法构造的散列表中,删除结点的操作易于实现。只要简单地删去链表 上相应的结点即可。