散列表简介:
散列表的实现常被称为散列。散列是一种用于以常数平均时间执行插入、删除和查找的技术。
散列的基本思想:
理想的散列表数据结构只不过是一个包含一些项的具有固定大小的数组。(表的大小一般为素数)
设该数组的大小为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值的冲突问题。
解决冲突的方法主要有:分离链接法和开放定址法
分离链接法:
思路是做法是将散列到同一个值的所有元素保留到一个链表中,该链表可以直接使用STL中的list类实现(该链表是双向链表)。
此时散列表的结构为: vector<list<HashedObj> > v;
散列表类的主要成员函数:
bool containes(const HashedObj & x) const;//判断是否包含数据项x
void makeEmpty();//清空散列表
bool isEmpty();
bool insert(const HashedObj & x);//插入项x
bool remove(const HashedObj & x);//删除项x
void print();//输出散列表中的内容
HashedObj findElement(const HashedObj & x);//根据名字查找数据项
void rehash();//再散列
int myhash(const HashedObj & x) const;//散列函数
int nextPrime(int n);//求的距离N最近的一个素数
主要成员函数介绍:
1、再散列rehash():
如果散列表的大小太小了就要进行再散列,在分离链接法中很简单,就是将散列表的大小扩大原来的两倍。然后将原来散列表的内容拷贝到新的散列表中。nextPrime函数是为了求的一个素数,因为一般我们让散列表的大小为一个素数。
/****************************************************************
* 函数名称:rehash()
* 功能描述: 扩大散列表的大小
* 参数列表: 无
* 返回结果:无
*****************************************************************/
template<typename HashedObj>
void HashTable<HashedObj>::rehash()
{
vector<list<HashedObj> > oldLists = theLists;
//创建一个新的大小为原来两倍大小的散列表
theLists.resize(nextPrime(2 * theLists.size()));
for(int i = 0; i < theLists.size(); ++i)
theLists[i].clear();
//复制散列表
for(int i = 0; i < oldLists.size(); ++i){
typename list<HashedObj>::iterator it = oldLists[i].begin();
while(it != oldLists[i].end())
insert(*it++);
}
}
2、散列函数myhash()
该函数会调用hash(key),使用了函数重载,目的是将不同类型的数据进行hash计算求hash值/****************************************************************
* 函数名称:myhash(const HashedObj & key)
* 功能描述: 根据键值求个hash值
* 参数列表: key 数据项的键值
* 返回结果:返回hash值
*****************************************************************/
template<typename HashedObj>
int HashTable<HashedObj>::myhash(const HashedObj & key) const
{
int hashVal = hash(key);
hashVal %= theLists.size();
if(hashVal < 0)
hashVal += theLists.size();
return hashVal;
}
3、插入函数insert(const HashedObj & x)
/****************************************************************
* 函数名称:insert(const HashedObj & x)
* 功能描述: 在散列表中插入元素x,如果插入项已经存在,则什么都不做。
* 否则将其放在表的前端
* 参数列表: x数据项
* 返回结果:插入成功返回true, 否则返回false
*****************************************************************/
template<typename HashedObj>
bool HashTable<HashedObj>::insert(const HashedObj & x)
{
list<HashedObj>