目录
引入
开散列的底层实现
哈希表的定义
哈希表的扩容
哈希表的插入
哈希表查找
哈希表的删除
引入
接上一篇,我们使用了闭散列的方法解决了哈希冲突,此篇文章将会使用开散列的方式解决哈希冲突,后面对unordered_set和unordered_map的封装也会用开散列的哈希表实现。
开散列,也叫哈希桶和拉链法,其数组中存储的不再是单个数据,而是一个个的节点指针。通过将数据映射到对应位置后,插入到链表中去。
闭散列就像一个个的链表挂在数组上。
开散列的底层实现
与闭散列不同的是,开散列哈希表中存储的是节点,不再是具体数据;也不许需要再存储状态,只需要存储下一个指针即可。
哈希表的定义
template<class K,class V>
struct HashNode
{
//默认构造
HashNode(const pair<K,V>& kv=pair())
:_kv(kv)
,next(nullptr)
{ }
pair<K, V> _kv;
HashNode* _next;
};
template<class K,class V>
class HashTable
{
typedef HashNode<K, V> Node;
public:
//默认构造
HashTable()
{
_table.resize(10); //初始情况下数组有10个空间
_n = 0;
}
private:
vector<Node*> _table;
size_t _n; //存储有效数据
};
哈希表的扩容
开散列在插入时,像闭散列一样也要检查载荷因子时候满足条件。开散列的载荷因子没有闭散列那么严格,开散列的载荷因子要求小于1即可,平均每条链有一个数据。
在扩容后也需要对数据进行重新插入。
扩容方法:创建新的哈希表,将原数据的节点一个个的插入到新数组中,再将两个数组进行交换。
//扩容
void More()
{
if ((double)_n / _table.size() >= 1)
{
//进行扩容
HashTable newtable;
size_t newsize = 2 * _table.size();
newtable._table.resize(newsize); //扩容两倍扩
size_t hashi = 0;
while (hashi < _table.size())
{
if (_table[hashi])
{
//将数据进行头插
Node* pcur = _table[hashi];
while (pcur)
{
Node* next = pcur->_next;
pcur->_next = newtable._table[hashi];
newtable._table[hashi] = pcur;
pcur = next;
}
}
hashi++;
}
_table.swap(newtable._table);
}
}
哈希表的插入
//插入
bool insert(const pair<K, V>& kv)
{
//扩容
More();
size_t hashi = kv.first % _table.size();
Node* newnode = new Node(kv);
//将数据头插到对应映射的位置
newnode->_next = _table[hashi];
_table[hashi] = newnode;
_n++;
return true;
}
哈希表查找
先找到映射的位置,在对应位置的链表中查找。
//查找
bool Find(const K& key)
{
size_t hashi = key % _table.size();
Node* pcur = _table[hashi];
while (pcur)
{
if (pcur->_kv.first == key)
{
return true;
}
pcur = pcur->_next;
}
return false;
}
哈希表的删除
哈希表的删除比较简单:直接将该节点的前后指针连起来即可。
//删除
bool Erase(const K& key)
{
size_t hashi = key % _table.size();
Node* pcur = _table[hashi];
Node* parent = nullptr;
while (pcur)
{
if (pcur->_kv.first == key)
{
if (parent == nullptr)
{
delete pcur;
pcur = nullptr;
_table[hashi] = nullptr;
}
else
{
parent->_next = pcur->_next;
delete pcur;
}
return true;
}
pcur = pcur->_next;
}
return false;
}
到此,哈希表的开散列的基本实现已经完成。还有一些具体细节将在《哈希表的封装》中进行具体分析。