散列表是实现字典的一种数据结构,是普通数组的推广,
原理是根据关键字计算出下标,用链接的方法解决冲突。
11.1 直接寻址表
如果关键字全集比较小,可以直接寻址。
关键是将key映射成为数字,是直接计算的。如果映射规则比较复杂,导致数字的范围很大,而有效的只有很少一部分时,直接寻址法就很蠢。
11.2 散列表
用散列函数h, 计算关键字k所在的位置为 h(k)。
这一步可以产生冲突,将不同的关键字映射到同一个槽内。
介绍两种解决冲突的方法:链接法、开放寻址法
链接法:
冲突链接到同一个链表中,
插入、删除的时间都是O(1),(删除不需要搜索)。双链表删除比较快。
查找性能?
n个元素,m个槽位,装载因子 n/m
只要槽位与元素数量成正比,那么查找时间就是O(1)
11.3 散列函数
好的散列函数的标准:均匀,相近的最好不在一起,
可以将关键字转换为自然数。以下的针对关键字为自然数的情况。
除法散列法: h(k) = k mod m。如果m是2的n次幂的话要小心。
乘法散列法:h(k) = m(kA mod 1) 其中A是一个0-1之间的小数,
优点:对m的选择不太关键,可以选择2的幂次,
全域散列法:每一个固定的散列方式都会导致存在一种状态,最差为n。
因此要随机选择散列函数。使之独立于要储存的keys。思想是从一开始,从一族散列函数中选择一个
全域散列族的定义为:对任意两个key,假设散列到m组中,发生冲突的散列函数 / 散列函数总数 < 1 / m
构造全域散列族:
输入:一个大于所有key的质数p,组数m
11.4 开放寻址法
在m组下面已经没有链表了,每个都是自己,因此装载因子小于1,
也就是m是大于整个数据量n的。
函数不是h(k) = j确定组,而是根据k和第几次尝试确定组,h(k, i) = j,要确保对于不同的i (从0到m),j是其一个全排列(从0到m)
在给出k之后,从i=0开始,看看哪一个有位置,
有三种方式:线性探查、二次探查、双重散列。
11.5 完全散列
待学习。