散列表.md

来源:互联网 发布:淘宝卖家如何发货 编辑:程序博客网 时间:2024/05/10 08:10

    • 散列表
    • 散列函数
    • 基于拉链法的散列表
    • 基于线性探测法的散列表

散列表

  • 目的:如果所有的键都是小整数,可以用一个数组来实现无序的符号表,将作为数组的索引而数组中键i处存储的就是它对应的值。
  • 查找算法步骤
    1. 散列函数将查找的函数转化为数组的一个索引。理想情况下,不同的减可以转换为不同的索引值。
    2. 处理碰撞冲突,分为拉链法线性探测法
  • 散列表是算法在时间和空间上的均衡处理方式。

散列函数

  • 散列函数特点:散列函数应该易于计算并且能够均匀分布所有的键,即对于任意的键,所给出的散列值应得呈随机性。
  • 散列函数与键的类型有关。
    1. 正整数:正整数散列最常用的方式为除留余数法。选择一个M的素数的数组,对于任意正整数k,计算k%M。如果不是素数,可能无法获得均匀的散列值。
    2. 浮点数:如果键值是0到1之间,可以将键值乘以素数M后,四舍五入得到一个0到M-1之间的索引值。但这样会使键的高位起作用更大,低位键值不影响散列值。解决办法是,键值用二进制表示后使用除留余数法
    3. 字符串:字符串散列同样使用除留余数法,将字符串考虑为大整数即可。Java的charAt()函数能够返回一个非负16位整数。如果R是比任何字符都大的整数,相当于,将字符串当做一个N位的R进制值,将它除以M求余。Horner方法,用N次乘法、加法和取余来计算一个字符串的散列值。只要R足够小,不造成溢出。
      java
      int hash = 0;
      for (int i = 0; i < s.length(); i++)
      hash = (R * hash + s.charAt(i)) % M
    4. 组合键:如果键的类型包含多个整形变量,我们可以和String类型一样将他们混合起来。例如,被查找的类型是Date,其中整型域:day(两位数)、month(两位数)和year(四位数),散列值是:
      Java
      int hash = (((day * R + month) % M) * R + year) % M;

      • 只要R足够小不造成溢出,也可以得到0到M-1之间的散列值。同时选取适当素数M值,例如31,来省去括号内的%M计算。
  • Java约定:即散列值的硬性规则。由于每种数据类型都需要相应的散列函数,于是Java让所有数据类型都继承了一个能够返回32bit整数的hashCode()方法。因此若a.equals(b)的值为ture,那么对应的hashCode值也应相同,若hashCode值不同,只能说明看似相同的数据,实则为不同数据类型。但同hashCode值,两个对象可能不同。
  • 将hashCode的返回值转化为数组索引:若我们想使用短的索引值,而不是32bit整数的散列值,这里利用hashCode()方法和除留余数法结合,产生0到M-1的整数。
  private int hash(Key x){      return (x.hashCode() & 0x7ffffffff) % M;  }
  • 自定义的hashCode()方法:可以按照将hashCode的返回值转化为短整数的方法,进行变换。将对象的每个变量的hashCode()返回值转化为32位整数并计算得到散列值。
public class Transaction{    ...    private final String who;    private final Date when;    private final double amount;    public int hashCode(){    int hash = 17;    hash = 31 * hash + who.hashCode();    hash = 31 * hash + when.hashCode();    hash = 31 * hash + ((Double) amount).hashCode();    return hash;    }    ...} 
  • 软缓存:如果散列值的计算很耗时,可以将计算的散列值存储在每个键的散列值函数里,即利用hash变量存储hashCode()的返回值。

基于拉链法的散列表

基于线性探测法的散列表