MIT算法导论公开课之第7课 哈希表

符号表问题

表S中有n条记录,每条记录都有一个键值,并且有其他的一些数据(卫星数据)。
允许的操作:
    1.插入数据(S,x):S ← S∪{x}
    2.删除数据(S,x):S ← S - {x}
    3.查询数据(S,k):存在键值k对应的数据x,则返回x,否则返回nil(空值)。
插入删除操作将S变为一个动态集合。

直接映射表

假设键值来自一个有m个元素的集合U{0,1,…,m-1},键值相互独立。
建立数组T[0,…,m-1]来表示动态集合S,当x∈S且x的键值为k时,T[k]=x,否则,当集合S中无此值
时,T[k]=nil(空值)。
在最坏情况下,其时间复杂度为Θ(1)。
算法的局限性主要体现在空间上,当S中的值是一个较大范围内数的集合的子集时,需要的空间很大,
并且空间的利用率低。

哈希法(Hashing)

用哈希函数H来“随机“将键值映射到表T中的槽(slot)中。

哈希法

当一条记录需要向一个已经插入记录的槽中插入内容时,即发生了碰撞(collision)。

链表法解决碰撞

把相同哈希值的记录放到一个链表里存储。

链表法解决碰撞

  • 运行时间分析:
    • 最坏情况:
      所有的键值都被映射到同一个槽中,哈希表退化为一个链表,操作的时间复杂度为Θ(n)。
    • 平均情况:
      • 简单均匀哈希,每个属于集合S的键值k都有相同的几率被哈希映射到表T的任意一个槽中,每个键与其他被哈希的记录或键之间相互独立。
      • 对于一个拥有m个槽的哈希表T来说,两个键值被哈希映射到同一个槽的概率为1/m,一个键值被哈希映射到特定某个槽的概率为1/m。
      • 定义一个存放n个键、有m个槽的哈希表,它的装载因子α=n/m,也就是每个槽中的平均数量。
      • 失败搜索的预计用时为Θ(1+α),“1“表示把键值哈希映射到槽的用时,”α“表示搜索槽对应的链表所花费的时间。当α=O(1),即n=O(m)(哈希表里键的数量不会超过槽的数量m的常数倍)时,搜索时间为Θ(1)。
      • 成功搜索的预计用时也是Θ(1+α)。

关于选择哈希函数

1.将键值均匀的分布到槽里。
2.键值本身的分布特性不应影响它在哈希表中分布的均匀性。
  • 除法哈希法

    h(k)=k mod m
    注:不要选择过小的m作为除数。
    
    • Ex:

      • m=2且所有的键值为偶数,则所有奇数槽都无法被用到,空间会被浪费一半。
      • m=2^r,则所有的键值哈希结果都只是取了键值二进制表示的低r位,又因为二进制数据通常具有一个常见的规则性,即数字的低位相同、高位不同或是相反,所以这种选取m的方法就存在问题。
    • 比较好的方法是选取不是很接近2的幂或10的幂的质数来作为m的值。

    • 缺点
      使用除法效率不高,因为相对于乘法和加法而言,在很多计算机上运算时,除法往往有更多的循环运算。
  • 乘法哈希法:

    假设m=2^r,计算机一个字的长度是w位。
    h(k)=(A·k mod 2^w) rsh (w-r)(2^(w-r)<A<2^w且A为奇数,rsh表示右移)。
    A的取值不要太接近以2为底的数。
    
    • 此算法的优点在于函数中的取余运算会比一般的除法快。

    • Ex:
      乘法哈希法

开放寻址法解决碰撞

主要思想是,插入元素时,系统地探查哈希表,直到找到一个空的槽为止。
哈希函数有两个参数,键值和探查号。
要求元素数量不超过槽的数量(n<=m)。
删除操作要特别处理,否则会影响探查过程。
  • Ex:
    插入k=496。
    开放寻址法解决碰撞示例
    如果是插入操作,则在槽中放入一条记录,如果是查询操作,则返回nil(空值)。
    • 探查策略:
      • 线性探查:
        h(k,i)=(h(k,0)+i) mod m
        缺点在于如果记录在槽中的存储存在集群现象,这种探查方法效率就会降低。
      • 二次哈希:
        h(k,i)=(h_1(k)+i*h_2(k)) mod m
        通常把m=2^r(取为以2为底的幂值),h_2(k)选择奇数的值。
        • 平均运行时间:
          假设所有的键值都均等的有m!种探查序列,每个键值的探查序列都相互独立。
          证明预期的探查次数最多不超过1/(1-α)(α<1)。
          证明预期的探查次数
          如果α是一个常数量,则只需O(1)步的探查。当哈希表T有50%有记录时,平均探查次数为2,当哈希表T有90%有记录时,平均探查次数为10。随着哈希表密度增大,探查的时间花费会急剧的增加。

猜你喜欢

转载自blog.csdn.net/rye_whiskey/article/details/81985873