目录
- [1 成员变量](#1 成员变量)
- [2 模板参数](#2 模板参数)
- [3 迭代器](#3 迭代器)
-
- [3.1 operator++](#3.1 operator++)
- [3.2 迭代器的代码](#3.2 迭代器的代码)
- [4 unordered_set](#4 unordered_set)
-
- [4.1 KeyofV](#4.1 KeyofV)
- [4.2 begin](#4.2 begin)
- [4.3 end](#4.3 end)
- [4.4 Insert](#4.4 Insert)
- [4.5 Erase](#4.5 Erase)
- [4.6 Find](#4.6 Find)
- [4.7 unordered_set 的代码](#4.7 unordered_set 的代码)
- [5 unordered_map](#5 unordered_map)
-
- [5.1 KeyofV](#5.1 KeyofV)
- [5.2 Insert](#5.2 Insert)
- [5.3 [] 重载](#5.3 [] 重载)
- [5.4 unordered_map 的代码](#5.4 unordered_map 的代码)
1 成员变量
unordered_map/unordered_set 是封装哈希表来实现的,所以成员变量只有一个哈希表
在 unordered_set 中,保存的值是不允许修改的,所以第二个模板参数需要加上 const
在 unordered_map 中,保存的键值对中,Key 是不允许修改的,所以需要给 Key 的类型加上 const
cpp
//unordered_set
HashTable<K, const K, KeyofV<K>> _t;
//unordered_map
HashTable<K, pair<const K, V>, KeyofV<K, V>> _t;
2 模板参数
哈希表的模板参数需要修改,由于 unordered_map 存储的是 pair 键值对 Key/Value,而 unordered_set 存储的就是Key,由于在进行值的比较时需要使用 Key,所以还需要增加一个 KeyofT 来取出 Key
cpp
template<class K, class V, class KeyofV, class Hash = HashFunc<K>>
迭代器的模板参数中,K 表示于比较的值的类型, V 表示存储的值的类型,Ptr 和 Ref 分别是指针类型和引用类型,用来实现普通迭代器和 const 迭代器,KeyofV 用于提取 Key ,Hash 用于将不符合要求的 Key 转化为整形(字符串,负数等)
cpp
template<class K, class V, class Ptr, class Ref, class KeyofV, class Hash = HashFunc<K>>
unordered_set 只需要存储一个值即可,所以模板参数只有一个 K,代表用于比较的值的类型和要存储的值的类型
cpp
template<class K>
unordered_map 要存储一个键值对 pair<K, V>,pair 中 Key 的类型为 K,Value 的类型为 V,所以模板参数有 K,V 两个
cpp
template<class K, class V>
3 迭代器
3.1 operator++
迭代器++ 后要移动到下一个结点,由于当前是哈希桶,所以实现方式有点类似于遍历链表,实现思路是在迭代器进行移动前先判断迭代器移动后会不会将当前的哈希桶遍历完(会不会指向空),若不会则让迭代器继续指向下一个结点即可,若会就要计算出当前哈希桶的编号 hashi,然后不断地增加编号,找到下一个不为空的哈希桶,如果所有的桶都遍历完了,编号 hashi 表示的就是无效桶,此时需要让迭代器指向空,代表到达哈希表尾
cpp
Self operator++()
{
if (_node->_next) //哈希桶中还有节点
{
_node = _node->_next;
}
else //哈希桶中没结点了
{
KeyofV kov;
Hash hash;
size_t hashi = hash(kov(_node->_kv)) % _pht->_table.size(); //计算当前桶的编号
hashi++;
while (hashi < _pht->_table.size())
{
if (_pht->_table[hashi]) //不是空桶,指向这个桶的第一个结点
{
_node = _table[hashi];
break;
}
hashi++; //是空桶移动到下一个桶
}
if (hashi == _pht->_table.size()) //所有的桶都遍历完了
_node = nullptr;
}
return *this;
}
3.2 迭代器的代码
cpp
template<class K, class V, class Ptr, class Ref, class KeyofV, class Hash = HashFunc<K>>
class Iterator
{
typedef HashNode<V> Node;
typedef HashTable<K, V, KeyofV, Hash> HashTable;
typedef Iterator<K, V, Ptr, Ref, KeyofV, Hash> Self;
public:
Iterator(Node* node, HashTable* pht)
:_node(node)
,_pht(pht)
{}
Ref operator*()
{
return _node->_kv;
}
Ptr operator->()
{
return &_node->_kv;
}
bool operator!=(const Self& it)
{
return it._node != _node;
}
Self operator++()
{
if (_node->_next) //哈希桶中还有节点
{
_node = _node->_next;
}
else //哈希桶中没结点了
{
KeyofV kov;
Hash hash;
size_t hashi = hash(kov(_node->_kv)) % _pht->_table.size(); //计算当前桶的编号
hashi++;
while (hashi < _pht->_table.size())
{
if (_pht->_table[hashi]) //不是空桶,指向这个桶的第一个结点
{
_node = _pht->_table[hashi];
break;
}
hashi++; //是空桶移动到下一个桶
}
if (hashi == _pht->_table.size()) //所有的桶都遍历完了
_node = nullptr;
}
return *this;
}
private:
Node* _node; //指向结点的指针
HashTable* _pht; //指向哈希表的指针
};
4 unordered_set
4.1 KeyofV
由于 unordered_set 中保存的值就是 Key,仿函数 KeyofV 直接返回该值即可
cpp
template<class K>
struct KeyofV
{
const K& operator()(const K& key)
{
return key;
}
};
4.2 begin
begin 的作用是返回第一个结点的迭代器,所以要遍历一下哈希表,找到第一个非空的哈希桶,返回这个哈希桶中的第一个结点的地址来构造出迭代器
cpp
iterator begin()
{
return _t.begin();
}
//哈希表的begin
iterator begin()
{
if (_n == 0)
return { nullptr, this };
for (int i = 0;i < _table.size();++i)
{
if (_table[i])
return { _table[i], this };
}
return { nullptr, this };
}
4.3 end
end 的作用是返回哈希表的表尾迭代器,所以返回空即可
cpp
iterator end()
{
return _t.end();
}
//哈希表的end
iterator end()
{
return nullptr;
}
4.4 Insert
Insert 的功能是插入结点,返回一个 pair,里面分别是指向结点的迭代器和 bool 类型的变量,true 表示插入成功,false 表示插入失败(已经有值)
cpp
pair<iterator, bool> Insert(const K& key)
{
return _t.Insert(key);
}
//哈希表的Insert
pair<iterator, bool> Insert(const V& kv)
{
KeyofV kov;
auto it = Find(kov(kv));
if (it != end())
return { it, false };
Hash hash;
//负载因子为1进行扩容
if (_n == _table.size())
{
//开新表
vector<Node*> newTable;
newTable.resize(__stl_next_prime(_table.size() + 1), nullptr);
//将原表的数据移动到新表内
for (int i = 0;i < _table.size();++i)
{
Node* cur = _table[i];
while (cur)
{
Node* next = cur->_next;
size_t hashi = hash(kov(cur->_kv)) % newTable.size();
cur->_next = newTable[hashi];
newTable[hashi] = cur;
cur = next;
}
_table[i] = nullptr;
}
}
size_t hashi = hash(kov(kv)) % _table.size();
Node* newNode = new Node(kv);
newNode->_next = _table[hashi];
_table[hashi] = newNode;
++_n;
return { { newNode, this }, true };
}
4.5 Erase
Erase 的功能是删除指定的结点,返回 bool 类型的值,true 代表删除成功,false 表示删除失败
cpp
bool Erase(const K& key)
{
return _t.Erase(key);
}
//哈希表的Erase
bool Erase(const K& key)
{
KeyofV kov;
Hash hash;
size_t hashi = hash(key) % _table.size();
Node* cur = _table[hashi];
Node* prev = nullptr;
while (cur)
{
if (kov(cur->_kv) == key && prev == nullptr)
{
_table[hashi]->_next = cur->_next;
--_n;
return true;
}
else if (kov(cur->_kv) == key && prev)
{
prev->_next = cur->_next;
--_n;
return true;
}
prev = cur;
cur = cur->_next;
}
return false;
}
4.6 Find
Find 要做的是根据传入的值来查找结点,找得到则返回该节点的迭代器,找不到返回哈希表的表尾迭代器
cpp
iterator Find(const K& key)
{
return _t.Find(key);
}
//哈希的Find
iterator Find(const K& key)
{
KeyofV kov;
Hash hash;
size_t hashi = hash(key) % _table.size();
Node* cur = _table[hashi];
while (cur)
{
if (kov(cur->_kv) == key)
return { cur, this };
cur = cur->_next;
}
return { end(), this };
}
4.7 unordered_set 的代码
cpp
template<class K>
class unordered_set
{
template<class K>
struct KeyofV
{
const K& operator()(const K& key)
{
return key;
}
};
public:
typedef typename HashTable<K, const K, KeyofV<K>>::iterator iterator;
typedef typename HashTable<K, const K, KeyofV<K>>::const_iterator const_iterator;
iterator begin()
{
return _t.begin();
}
iterator end()
{
return _t.end();
}
const_iterator begin() const
{
return _t.begin();
}
const_iterator end() const
{
return _t.end();
}
pair<iterator, bool> Insert(const K& key)
{
return _t.Insert(key);
}
iterator Find(const K& key)
{
return _t.Find(key);
}
bool Erase(const K& key)
{
return _t.Erase(key);
}
private:
HashTable<K, const K, KeyofV<K>> _t;
};
5 unordered_map
begin,end,Erase,Find 与 unordered_set 类似,不在这里说明
5.1 KeyofV
由于 unordered_map 中保存的是 pair,进行比较时要用其中的 Key,所以需要使用仿函数 KeyofV 取出它的 Key
cpp
template<class K, class V>
struct KeyofV
{
const K& operator()(const pair<K, V>& kv)
{
return kv.first;
}
};
5.2 Insert
Insert 使用时,除了插入的是一个 pair,返回值和功能都与 unordered_set 是一样的
cpp
pair<iterator, bool> Insert(const pair<K, V>& kv)
{
return _t.Insert(kv);
}
5.3 [] 重载
\] 重载兼具了查找,修改和插入的作用,所以要复用哈希表的 Insert 函数,根据外部传入的 key,\[\] 重载内将 Key 和 V 类型的默认值构成 pair ,调用哈希表的 Insert 来进行插入,在插入时会进行查找,找得到时,返回的 pair 内保存的是指向该节点的迭代器和 false,找不到时会插入新结点,返回的 pair 内保存的是指向新插入结点的迭代器和 true,再通过该迭代器返回结点中值的引用即可,这样外部就可以做到修改值
```cpp
V& operator[](const K& key)
{
pair