线性探测法
一般说来,对于不使用分离链接的散列表来说,其装填因子应该低于 λ=0.5,这样的表叫作探测散列表(probing hash table)。

如上图所示,当插入关键字68时,h(68)=68%11=2,与23冲突,于是被放入到下一个空闲位置4;插入11时,h(11)=0,与55冲突,向后寻找并存入空闲位置5。。。。只要表足够大,总能找到一个空闲位置,但花费的时间是相当多的。
可以看出,插入和不成功查找需要相同次数的探测,且成功查找应该比不成功查找平均花费较少的时间。
平方探测法
平方探测法是消除线性探测中一次聚集问题的冲突解决办法 – 冲突函数为二次的探测方法,流行的选择是f(i)=i2。
如果使用平方探测,且表的大小是素数,那么当表至少有一半是空的时候,总能够插入一个新的元素。
再散列
对于使用平方探测的开放定址散列法(open addressing hashing),若散列表装的太满,则操作的运行时间将消耗过长,且插入操作可能失败,这可能发生在有太多的删除和插入混杂的场合。
此时,一种解决办法是建立另外一个大约两倍大的表(而且使用一个相关的新散列函数),扫描整个原始散列表,计算每个(未删除的)元素的新散列值并将其插入到新表中。
再散列可以用平方探测以多种方法实现:
(1)只要表满到一半就再散列
(2)只有当插入失败时才再散列
(3)途中策略:当散列表到达某一特定的装填因子时进行再散列。
代码实现
#include<vector>
#include<string>
int nextPrime(int n);
//使用函数对象,让散列函数只用对象作为参数并返回适当的整形量
template<typename T>
class _hash
{
public:
size_t operator()(const T& key) {
size_t hashVal = 0;
T tmp = key;
while (tmp > 0) {
hashVal = 37 * hashVal + tmp % 10;
tmp /= 10;
}
return hashVal;
}
};
template<>
class _hash<std::string>
{
public:
size_t operator()(const std::string & key) {
size_t hashVal = 0;
for (char ch : key)
hashVal = 37 * hashVal + ch;
return hashVal;
}