数据结构:散列1(分离链接法)
散列:
将每个键映射到从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; } };
人生如戏,还是戏如人生?微信公众号:传说之路
csdn博客 http://blog.csdn.net/u012175089/article/list/2