扫二维码与项目经理沟通
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流
前面几篇博客已经写过了哈希表的闭散列法,也写过哈希表的应用,在这里就不赘述。今天我们要实现的是一个哈希桶。什么哈希桶呢?
创新互联公司专注于企业全网整合营销推广、网站重做改版、定襄网站定制设计、自适应品牌网站建设、H5页面制作、商城网站建设、集团公司官网建设、外贸营销网站建设、高端网站制作、响应式网页设计等建站业务,价格优惠性价比高,为定襄等各大城市提供网站开发制作服务。哈希桶:哈希桶就是盛放不同key链表的容器(即是哈希表),在这里我们可以把每个key的位置看作是一个孔,孔里放了一个链表
相信大家可以看出来,使用一个数组来存放记录方法的哈希冲突太多,基于载荷因子的束缚,空间利用率不高,在需要节省空间的情况下,我们可以用哈希桶来处理哈希冲突。
哈希桶是使用一个顺序表来存放具有相同哈希值的key的链表的头节点,利用这个头节点可以找到其它的key。
#pragma once #include#include #include using namespace std; template struct DefaultFunc { size_t operator()(const T& data) { return (size_t)data; } }; struct StringFunc { size_t operator()(const string& str) { size_t sum = 0; for (size_t i = 0; i < str.size(); ++i) { sum += str[i]; } return sum; } }; template struct HashBucketNode { K _key; V _value; HashBucketNode * _next; HashBucketNode(const K& key, const V& value) :_key(key) , _value(value) , _next(NULL) {} }; template > class HashBucket { typedef HashBucketNode Node; public: HashBucket(); //~HashBucket(); HashBucket(const HashBucket & h); HashBucket & operator=(HashBucket h); bool Insert(const K& key, const V& value); Node* Find(const K& key); bool Remove(const K& key); //bool Alter(const K& key);//用Find实现 void Print(); protected: void _CheckExpand();//检查是否需要扩容 size_t _GetNextPrime(size_t size);//从素数表中得到比当前素数大的素数 size_t _HashFunc(const K& key,size_t mod);//哈希函数 protected: vector _table; size_t _size;//记录的个数 };
得到哈希桶的结构以后,我们来实现几个比较重要的函数:
(一)bool Insert(const K& key, const V& value)
插入函数是最难实现的,它涉及到是否需要扩容。为插入函数我们写了两个内部函数void _CheckExpand(); 和 size_t _GetNextPrime(size_t size);
templatebool HashBucket ::Insert(const K& key, const V& value) { _CheckExpand(); //使用头插法插入 size_t index = _HashFunc(key,_table.size()); Node* tmp=new Node(key, value);//一定要用new出结点 if (_table[index] == NULL) { _table[index] = tmp; } else { //不为NULL则使用头插法插入新结点 tmp->_next = _table[index]; _table[index] = tmp; } _size++; return true; } template size_t HashBucket ::_GetNextPrime(size_t size) { const int _PrimeSize = 28; static const unsigned long _PrimeList[_PrimeSize] = { 53ul, 97ul, 193ul, 389ul, 769ul, 1543ul, 3079ul, 6151ul, 12289ul, 24593ul, 49157ul, 98317ul, 196613ul, 393241ul, 786433ul, 1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul, 50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul, 1610612741ul, 3221225473ul, 4294967291ul }; for (int i = 0; i < _PrimeSize; ++i) { if (_PrimeList[i] > size) return _PrimeList[i]; } assert(false); return 4294967291ul; } template void HashBucket ::_CheckExpand() { if (_size == _table.size()) { size_t newsize = _GetNextPrime(_size);//从素数表中取出下一个素数 if (newsize == _size) return; vector newtable; newtable.resize(newsize); for (size_t i = 0; i < _size; ++i) { size_t index = _HashFunc(_table[i]->_key,newsize); if (_table[i]) { Node* head = _table[i];//保存头节点 newtable[index] = head;//摘下vector的第一个指针为新表的头节点 _table[i] = _table[i]->_next; while (_table[i])//依次摘结点 { Node* tmp = _table[i]; _table[i] = _table[i]->_next; head->_next = tmp; head = head->_next; } } else newtable[index] = NULL; _table[i] = NULL; } swap(_table, newtable); } }
在扩容的函数中我们使用了素数表,有大师证明Mod素数表中的素数可以减少哈希冲突,其实我也感觉很奇妙。
在调用哈希函数HashFunc的时候一定要注意,我们用key取模一定模的是vector当前的容量。
在插入的时候使用头插法是很高效的!!!
(二)Node* Find(const K& key);
查找函数返回一个结点的指针方便我们在外部更改数据。
emplateHashBucketNode * HashBucket ::Find(const K& key) { size_t index = _HashFunc(key, _table.size()); while (_table[index]) { if (_table[index]->_key == key) { return _table[index]; } _table[index] = _table[index]->_next; } return NULL; }
(三)bool Remove(const K& key);
我们在写删除节点函数时最好别调用Find函数去查找要删除的结点,如果要删除的结点是头节点或者尾节点的话就无法修改要删除指针的前一个指针的_next域;
templatebool HashBucket ::Remove(const K& key) { //不能用find找到,然后删。 size_t index = _HashFunc(key,_table.size()); if (_table[index] == NULL) return false; Node* cur = _table[index]; if (cur->_key==key) { Node* del = cur; _table[index] = cur->_next; delete del; _size--; return true; } else { Node* prev = NULL; while (cur) { if (cur->_key == key) { prev->_next = cur->_next; delete cur; _size--; return true; } prev = cur; cur = cur->_next; } return false; } }
其他的函数比较的简单,在这里我就不写出来了;
另外有需要云服务器可以了解下创新互联scvps.cn,海内外云服务器15元起步,三天无理由+7*72小时售后在线,公司持有idc许可证,提供“云服务器、裸金属服务器、高防服务器、香港服务器、美国服务器、虚拟主机、免备案服务器”等云主机租用服务以及企业上云的综合解决方案,具有“安全稳定、简单易用、服务可用性高、性价比高”等特点与优势,专为企业上云打造定制,能够满足用户丰富、多元化的应用场景需求。
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流