传统艺能😎
小编是双非本科大一菜鸟不赘述,欢迎各位指点江山(期待~)(QQ:1319365055)
此前博客点我!点我!请搜索博主 【知晓天空之蓝】
🎉🎉非科班转码社区诚邀您入驻🎉🎉
小伙伴们,打码路上一路向北,彼岸之前皆是疾苦
一个人的单打独斗不如一群人的砥砺前行
这是和梦想合伙人组建的社区,诚邀各位有志之士的加入!!
社区用户好文均加精(“标兵”文章字数2000+加精,“达人”文章字数1500+加精)
直达: 社区链接点我
如果你看了上一篇用红黑树封装实现 map 和 set ,那么本文就比较熟悉了因为二者的思路是一样的,但是别看这里是哈希表,哈希表实现虽然没有红黑树麻烦,但是 unordered_map 和 unordered_set 的封装却要更加复杂一点。
和封装 map,set 一样,这里用一个 KV 模型来讲解,首先还是给出哈希表源码(有疑问的友友参见我上一篇文章):
template<class K, class V>
struct HashNode
{
pair<K, V> _kv;
HashNode<K, V>* _next;
//构造函数
HashNode(const pair<K, V>& kv)
:_kv(kv)
, _next(nullptr)
{
}
};
//哈希表
template<class K, class V>
class HashTable
{
typedef HashNode<K, V> Node; //哈希结点类型
public:
//获取本次增容后哈希表的大小
size_t GetNextPrime(size_t prime)
{
const int PRIMECOUNT = 28;
//素数序列
const size_t primeList[PRIMECOUNT] =
{
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
};
size_t i = 0;
for (i = 0; i < PRIMECOUNT; i++)
{
if (primeList[i] > prime)
return primeList[i];
}
return primeList[i];
}
//插入函数
bool Insert(const pair<K, V>& kv)
{
//1、查看哈希表中是否存在该键值的键值对
Node* ret = Find(kv.first);
if (ret) //哈希表中已经存在该键值的键值对(不允许数据冗余)
{
return false; //插入失败
}
//2、判断是否需要调整哈希表的大小
if (_n == _table.size()) //哈希表的大小为0,或负载因子超过1
{
//增容
//a、创建一个新的哈希表,新哈希表的大小设置为原哈希表的2倍(若哈希表大小为0,则将哈希表的初始大小设置为10)
vector<Node*> newtable;
newtable.resize(GetNextPrime(_table.size()));
//b、将原哈希表当中的结点插入到新哈希表
for (size_t i = 0; i < _table.size(); i++)
{
if (_table[i]) //桶不为空
{
Node* cur = _table[i];
while (cur) //将该桶的结点取完为止
{
Node* next = cur->_next; //记录cur的下一个结点
size_t index = cur->_kv.first%newtable.size(); //通过哈希函数计算出对应的哈希桶编号index(除数不能是capacity)
//将该结点头插到新哈希表中编号为index的哈希桶中
cur->_next = newtable[index];
newtable[index] = cur;
cur = next; //取原哈希表中该桶的下一个结点
}
_table[i] = nullptr; //该桶取完后将该桶置空
}
}
//c、交换这两个哈希表
_table.swap(newtable);
}
//3、将键值对插入哈希表
size_t index = kv.first % _table.size(); //通过哈希函数计算出对应的哈希桶编号index(除数不能是capacity)
Node* newnode = new Node(kv); //根据所给数据创建一个待插入结点
//将该结点头插到新哈希表中编号为index的哈希桶中
newnode->_next = _table[index];
_table[index] = newnode;
//4、哈希表中的有效元素个数加一
_n++;
return true;
}
//查找函数
HashNode<K, V>* Find(const K& key)
{
if (_table.size() == 0) //哈希表大小为0,查找失败
{
return nullptr;
}
size_t index = key % _table.size(); //通过哈希函数计算出对应的哈希桶编号index(除数不能是capacity)
//遍历编号为index的哈希桶
HashNode<K, V>* cur = _table[index];
while (cur) //直到将该桶遍历完为止
{
if (cur->_kv.first == key) //key值匹配,则查找成功
{
return cur;
}
cur = cur->_next;
}
return nullptr; //直到该桶全部遍历完毕还没有找到目标元素,查找失败
}
//删除函数
bool Erase(const K& key)
{
//1、通过哈希函数计算出对应的哈希桶编号index(除数不能是capacity)
size_t index = key % _table.size();
//2、在编号为index的哈希桶中寻找待删除结点
Node* prev = nullptr;
Node* cur = _table[index];
while (cur) //直到将该桶遍历完为止
{
if (cur->_kv.first == key) //key值匹配,则查找成功
{
//找到了待删除结点,则删除该结点
if (prev == nullptr) //待删除结点是哈希桶中的第一个结点
{
_table[index] = cur->_next; //将第一个结点从该哈希桶中移除
}
else //待删除结点不是哈希桶的第一个结点
{
prev->_next = cur->_next; //将该结点从哈希桶中移除
}
delete cur;
//删除结点后,有效元素个数减一
_n--;
return true; //删除成功
}
prev = cur;
cur = cur->_next;
}
//假删除可能会导致迭代器失效
return false; //直到该桶全部遍历完毕还没有找到待删除元素,删除失败
}
private:
vector<Node*> _table;
size_t _n = 0; //有效元素个数
};
模板参数🤔
老规矩,和 map,set 同理,unordered_set 是 K 模型的容器,而 unordered_map 是 KV 模型的容器,既然是实现同时封装,那么模板参数就必须进行控制,先给出哈希表的模板&#x