散列:
将每个键映射到从0到TableSize-1这个范围的某个数,并将其放到适当的单元中。这个映射成为散列函数
理想情况下,不同的键映射到不同的单元,但是现实中是不可能的,所以好的散列函数,应该尽量均匀地分配键。
列表的大小最好是素数,这个非常非常重要。
解决冲突:
冲突:如果一个元素插入时与一个已经插入的元素散列到相同的值,那么就产生了一个冲突。
解决冲突的方法:分离链接法和探测法
分离链接法:将散列到的同一个值的所有元素保留到一个链表中。确定是要分配内存。
对于List和Vector,都是使用自己实现的简单模板。Vector模板的实现 List模板的实现
探测法在下一章:数据结构:散列2(探测法)
先看看一些公用的函数:
//通过迭代器查找相同的对象
template<class InputIterator, class T>
InputIterator find(InputIterator first, InputIterator last, const T& val)
{
while (first != last)
{
if (*first == val) return first;
++first;
}
return last;
}
//是否一个素数
bool isPrime(int num)
{
if (num == 2 || num == 3)
{
return true;
}
if (num % 6 != 1 && num % 6 != 5)
{
return false;
}
for (int i = 5; i*i <= num; i += 6)
{
if (num % i == 0 || num % (i + 2) == 0)
{
return false;
}
}
return true;
}
//查找比x大的第一个素数
int nextPrime(int n)
{
bool state = isPrime(n);
while (!state)
{
state = isPrime(++n);
}
return n;
}
//string的散列函数
int hash(const std::string& key)
{
int hashVal = 0;
for (size_t i = 0; i < key.length(); i++)
{
hashVal = 37 * hashVal + key[i];
}
return hashVal;
}
//int的散列函数
int hash(int key)
{
return key;
}
使用分离链接法的代码:
//分离链接法
template <typename HashedObj>
class HashTable
{
public:
//构造函数
explicit HashTable(int size = 101) :theList(size){}
//包含某个对象
bool contains(const HashedObj& x)const
{
const List<HashedObj>& whichList = theList[myhash(x)];
return find(whichList.begin(), whichList.end(), x) != whichList.end();
}
//清空散列表
void makeEmpty()
{
for (int i = 0; i < theList.size(); i++)
{
theList[i].clear();//对每个散列表清空
}
}
//插入数据
bool insert(const HashedObj& x)
{
//找到散列表中对应位置的链表
List<HashedObj>& whichList = theList[myhash(x)];
//在链表中找到x
List<HashedObj>::iterator iter = find(whichList.begin(), whichList.end(), x);
if (iter != whichList.end())
{
return false;
}
whichList.push_back(x);
++currentSize;
//数据的大小超过了散列表的大小,由此可见,理想情况下,散列表下的链表应该是只放一个数据是最好的
if (currentSize > theList.size())
{
rehash();//重构一个更加大的散列表
}
return true;
}
//删除数据
void remove(const HashedObj& x)
{
//找到链表
List<HashedObj>& whichList = theList[myhash(x)];
List<HashedObj>::iterator iter = find(whichList.begin(), whichList.end(), x);
if (iter == whichList.end())
{
return false;
}
//删除
whichList.erase(iter);
--currentSize;
return true;
}
private:
Vector<List<HashedObj>> theList;//散列表
int currentSize;//当前数据量
//重新构造一个散列表
void rehash()
{
//原来的散列表
Vector<List<HashedObj>> oldList = theList;
//散列表的大小扩大两倍,然后再找接下来的第一个素数
theList.resize(nextPrime(2 * theList.size()));
//清空散列表
for (int i = 0; i < theList.size(); i++)
{
theList[i].clear();
}
//插入旧的数据
currentSize = 0;
for (int i = 0; i < oldList.size(); i++)
{
List<HashedObj>::iterator iter = oldList[i].begin();
while (iter != oldList[i].end())
{
insert(*iter);
iter++;
}
}
}
//计算散列数
int myhash(const HashedObj& x)const
{
int hashVal = hash(x);
hashVal %= theList.size();
if (hashVal < 0)
{
hashVal += theList.size();
}
return hashVal;
}
};