散列:
将每一个键映照到从0到TableSize⑴这个范围的某个数,并将其放到适当的单元中。这个映照成为散列函数
理想情况下,不同的键映照到不同的单元,但是现实中是不可能的,所以好的散列函数,应当尽可能均匀地分配键。
列表的大小最好是素数,这个非常非常重要。
解决冲突:
冲突:如果1个元素插入时与1个已插入的元素散列到相同的值,那末就产生了1个冲突。
解决冲突的方法:分离链接法和探测法
分离链接法:将散列到的同1个值的所有元素保存到1个链表中。肯定是要分配内存。
对List和Vector,都是使用自己实现的简单模板。Vector模板的实现 List模板的实现
探测法在下1章:数据结构:散列2(探测法)
先看看1些公用的函数:
//通过迭代器查找相同的对象 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; } //是不是1个素数 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大的第1个素数 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; //数据的大小超过了散列表的大小,因而可知,理想情况下,散列表下的链表应当是只放1个数据是最好的 if (currentSize > theList.size()) { rehash();//重构1个更加大的散列表 } 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;//当前数据量 //重新构造1个散列表 void rehash() { //原来的散列表 Vector<List<HashedObj>> oldList = theList; //散列表的大小扩大两倍,然后再找接下来的第1个素数 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; } };