开散列
- 开散列又叫链地址法,首先对关键码集合用散列函数计算散列地址,具有相同地址的关键码归于同一子集合,每一个子集合成为一个桶,各个桶中的元素通过一个单链表链接起来,各链表的头节点存储在哈希表中。
通过结构的分析我们不难看出使用开散列比开放定址法的好处在于减少了因哈希冲突而导致的额外寻找,在某中意义上实现了存储位置与关键字的真正对应,但是开散列仍然需要扩容,也就是还需要负载因子,因为可能很多数据映射在同一个桶中导致一个链表过长,扩容的目的可以降低各个桶的长度,从而增加查找速度
- 代码实现
//使用开散列来实现hash
namespace openlist{
template<typename K, typename V>
struct HashNode{
HashNode(const pair<K, V>& data)
:_data(data),
_next(nullptr)
{}
pair<K, V> _data;
shared_ptr<HashNode> _next;
};
template<typename K, typename V, typename hash = HashFunc<K>>
class Hash{
private:
using Node = HashNode<K,V>;
public:
Hash():_n(0) { _table.resize(10); }
bool insert(const pair<K, V>& kv);
Node* find(const K& key);
bool erase(const K& key);
private:
vector<shared_ptr<Node>> _table;
size_t _n = 0; //记录插入的个数,可以用来计算平衡因子
};
template<typename K, typename V, typename hash>
HashNode<K,V>* Hash<K, V, hash>::find(const K& key){
hash hs;
size_t sz = _table.size(), hashi = hs(key) % sz;
//当这个桶执行的元素历遍完了,如果还是没有找到就证明没有这个元素
shared_ptr<Node> cur = _table[hashi];
while(cur){
if(cur->_data.first == key) return HashIterator(this, cur);
else cur = (cur->_next);
}
return HashIterator(this, nullptr);
}
//这里扩容时有一个效率的问题,就是我们是像开放定址法一样新创建一个_table
//然后逐渐进行插入吗,如果是这样的化,那么效率太低,所以可以重复利用原来创建好的指针
template<typename K, typename V, typename hash>
bool Hash<K,V,hash>::insert(const pair<K, V>& kv){
HashIterator node = find(kv.first);
if(node) ;
hash hs;
size_t old_sz = _table.size();
//当桶中元素的数量和桶的数量相同时进行扩容操作
if(_n == old_sz){
size_t new_sz = old_sz*2;
vector<shared_ptr<Node>> new_table;
new_table.resize(new_sz);
for(int i = 0; i < old_sz; ++i){
//unique<Node>
auto cur = _table[i];
while(cur){
size_t hashi = hs(cur->_data.first) % new_sz;
auto next = (cur->_next);
shared_ptr<Node> ptr = new_table[hashi];
cur->_next = ptr;
new_table[hashi] = move(cur);
cur = next;
}
}
_table.swap(new_table);
}
//插入新节点
size_t hashi = hs(kv.first) % _table.size();
shared_ptr<Node> old_node = _table[hashi];
shared_ptr<Node> new_node = make_shared<Node>(kv);
new_node->_next = move(old_node);
_table[hashi] = move(new_node);
_n++;
}
template<typename K, typename V, typename hash>
bool Hash<K,V,hash>::erase(const K& key){
hash hs;
size_t sz = _table.size(), hashi = hs(key)%sz;
shared_ptr<Node> cur = _table[hashi];
shared_ptr<Node> prev;
while(cur){
if(cur->_data.first == key){
if(!prev) {
_table[hashi] = move(cur->_next);
}
else{
prev->_next = move(cur->_next);
}
return true;
}
prev = cur;
cur = cur->_next;
}
return false;
}
}