散列表简介:
散列表的实现常被称为散列。散列是一种用于以常数平均时间执行插入、删除和查找的技术。
散列的基本思想:
理想的散列表数据结构只不过是一个包含一些项的具有固定大小的数组。(表的大小一般为素数)设该数组的大小为TbaleSize,我们向该散列表中插入数据,首先我们将该数据用一个函数(散列函数)映射一个数值x(位于0到TbaleSize1-1之间);然后将该数据插入到散列表的第x的单元。(如果有多个数据映射到同一个数值,这个时候就会发生冲突)
散列函数介绍:
为了避免散列函数生成的值不是均匀分布,有一个比较好的散列函数可以使用。
在该散列函数中涉及数据中所有的字符,并且一般可以分布的很好,它计算的是0到KeySize-1进行求和Key[KeySize-i-1]*(37^i);
下面是该散列函数的实现:
/****************************************************************
* 函数名称:hash(const string & key, int tableSize)
* 功能描述: 根据键值求个hash值
* 参数列表: key 数据项的键值
* tableSize 散列表的大小
* 返回结果:返回hash值
*****************************************************************/
int hash(const string & key, int tableSize)
{
int hashVal = 0;
//用散列函数的那个公式求和
for(int i = 0; i < key.length(); ++i)
hashVal = 37*hashVal + key[i];
hashVal %= tableSize;//求得的hash值
if(hashVal < 0)
hashVal += tableSize;
return hashVal;
}
散列函数完成之后下面就是解决hash值的冲突问题。
解决冲突的方法主要有:分离链接法和开放定址法
分离链接法:分离链接法
开放定址法:
发生冲突的时候分离链接法需要给新的单元分配新的地址空间,这会导致算法导致算法速度减慢。还有另外一种思路可以解决hash值的冲突问题,当发生冲突的时候就尝试选择另外一个单元,直到找到空的单元。也就是依次尝试h0(x), h1(x), h2(x).....,其中hi(x) = (hash(x) + f(i)) % TableSize,且f(0)=0,直到找到一个hi(x)单元是空的。函数f是冲突解决函数。
因为此办法是将所有的元素都放入散列表中,分离链接法是将有相同hash值的元素放在一个链表里面,所以该方法要比分离链接法需要的散列表大。
对于此方法的解决冲突方案需要的散列表的装填因子应该低于0.5。装填因子是散列表中的元素个数与散列表的大小的比值。所以该方法需要的
散列表的大小应该大于等于元素个数的两倍。
我们称使用此方法的散列表为探测散列表(probing hash tables).
探测散列表有三种常见的解决冲突的方法:线性探测、平方探测、双散列。
线性探测:
线性探测中函数f是i的线性函数,一般情况下为f(i) = i。相当于是逐个探测散列表的单元直到查找出空单元。
下图是一个例子:
在上面的例子中,第一个冲突发生在插入49的时候,h0(49)=9,但是此时第9个位置已经被占用了,所以只能继续探测,直到找到一个空的位置。下一次探