目录
2、unordered_set和unordered_map的插入
1、实现哈希表的泛型
我们先引入上一节中的哈希表,这里使用的是开散列的哈希表
namespace hash_bucket
{
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>
struct HashFunc
{
size_t operator()(const K& key)
{
return (size_t)key;
}
};
template<> // 对string类型进行特化
struct HashFunc<string>
{
size_t operator()(const string& s)
{
size_t hash = 0;
for (auto e : s)
{
hash *= 31;
hash += e;
}
return hash;
}
};
template<class K, class V, class Hash = HashFunc<K>>
class HashTable
{
typedef HashNode<K, V> Node;
public:
HashTable()
{
_tables.resize(10, nullptr);
}
~HashTable()
{
for (size_t i = 0; i < _tables.size(); i++)
{
Node* cur = _tables[i];
while (cur)
{
Node* next = cur->_next;
delete cur;
cur = next;
}
_tables[i] = nullptr;
}
}
bool Insert(const pair<K, V>& kv)
{
if (Find(kv.first))
return false;
Hash hs;
size_t hashi = hs(kv.first) % _tables.size();
// 负载因子==1,扩容
if (_n == _tables.size())
{
vector<Node*> newtables(_tables.size() * 2, nullptr);
for (size_t i = 0; i < _tables.size(); i++)
{
Node* cur = _tables[i];
while (cur)
{
Node* next = cur->_next;
// 旧表中的结点,挪动到新表重新映射的位置
size_t hashi = hs(cur->_kv.first) % newtables.size();
// 头插到新表
cur->_next = newtables[hashi];
newtables[hashi] = cur;
cur = next;
}
// 原来的桶取完后要置空
_tables[i] = nullptr;
}
_tables.swap(newtables);
}
// 头插
Node* newNode = new Node(kv);
newNode->_next = _tables[hashi];
_tables[hashi] = newNode;
++_n;
return true;
}
Node* Find(const K& key)
{
Hash hs;
size_t hashi = hs(key) % _tables.size();
Node* cur = _tables[hashi];
while (cur)
{
if (cur->_kv.first == key)
return cur;
cur = cur->_next;
}
return nullptr;
}
bool Erase(const K& key)
{
Hash hs;
size_t hashi = hs(key) % _tables.size();
Node* prev = nullptr;
Node* cur = _tables[hashi];
while (cur)
{
if (cur->_kv.first == key)
{
if (prev == nullptr)
_tables[hashi] = cur->_next;
else
prev->_next = cur->_next;
delete cur;
return true;
}
prev = cur;
--_n;
cur = cur->_next;
}
return false;
}
private:
vector<Node*> _tables;
size_t _n = 0; // 表中存储数据个数
};
}
在这个哈希表中,结点中默认存储的类型是pair,这是unordered_map中的数据类型,并不符合unordered_set.通过观察STL源码可知,unordered_set和unordered_map使用的是同一个哈希表类模板,所以我们需要对哈希表进行改造,使哈希表可以通过模板参数来控制结点中存放的数据类型
namespace hash_bucket
{
template<class T>
struct HashNode
{
T _data;
HashNode<T>* _next;
HashNode(const T& data)
:_data(data)
,_next(nullptr)
{}
};
template<class K>
struct HashFunc
{
size_t operator()(const K& key)
{
return (size_t)key;
}
};
template<> // 对string类型进行特化
struct HashFunc<string>
{
size_t operator()(const string& s)
{
size_t hash = 0;
for (auto e : s)
{
hash *= 31;
hash += e;
}
return hash;
}
};
template<class K, class T, class KeyOfT, class Hash = HashFunc<K>>
class HashTable
{
typedef HashNode<T> Node;
public:
HashTable()
{
_tables.resize(10, nullptr);
}
~HashTable()
{
for (size_t i = 0; i < _tables.size(); i++)
{
Node* cur = _tables[i];
while (cur)
{
Node* next = cur->_next;
delete cur;
cur = next;
}
_tables[i] = nullptr;
}
}
bool Insert(const T& data)
{
Hash hs;
KeyOfT kot;
if (Find(kot(data)))
return false;
size_t hashi = hs(kot(data)) % _tables.size();
// 负载因子==1,扩容
if (_n == _tables.size())
{
vector<Node*> newtables(_tables.size() * 2, nullptr);
for (size_t i = 0; i < _tables.size(); i++)
{
Node* cur = _tables[i];
while (cur)
{
Node* next = cur->_next;
// 旧表中的结点,挪动到新表重新映射的位置
size_t hashi = hs(kot(cur->_data)) % newtables.size();
// 头插到新表
cur->_next = newtables[hashi];
newtables[hashi] = cur;
cur = next;
}
// 原来的桶取完后要置空
_tables[i] = nullptr;
}
_tables.swap(newtables);
}
// 头插
Node* newNode = new Node(data);
newNode->_next = _tables[hashi];
_tables[hashi] = newNode;
++_n;
return true;
}
Node* Find(const K& key)
{
Hash hs;
KeyOfT kot;
size_t hashi = hs(key) % _tables.size();
Node* cur = _tables[hashi];
while (cur)
{
if (kot(cur->_data) == key)
return cur;
cur = cur->_next;
}
return nullptr;
}
bool Erase(const K& key)
{
Hash hs;
KeyOfT kot;
size_t hashi = hs(key) % _tables.size();
Node* prev = nullptr;
Node* cur = _tables[hashi];
while (cur)
{
if (kot(cur->_data) == key)
{
if (prev == nullptr)
_tables[hashi] = cur->_next;
else
prev->_next = cur->_next;
delete cur;
return true;
}
prev = cur;
--_n;
cur = cur->_next;
}
return false;
}
private:
vector<Node*> _tables;
size_t _n = 0; // 表中存储数据个数
};
}
unordered_set和unordered_map类定义为
namespace cxf
{
template<class K>
class unordered_set
{
struct SetKeyOfT
{
const K& operator()(const K& key)
{
return key;
}
};
private:
hash_bucket::HashTable<K, K, SetKeyOfT> _ht;
};
}
namespace cxf
{
template<class K, class V>
class nuordered_map
{
struct MapKeyOfT
{
const K& operator()(const pair<K, V>& kv)
{
return kv.first;
}
};
private:
hash_bucket::HashTable<K, pair<K, V>, MapKeyOfT> _ht;
};
}
2、unordered_set和unordered_map的插入
在上面,我们以及给哈希表增加了模板参数KeyOfT。这个模板参数是一个仿函数,就是为了当结点的数据类型是pair时,取出里面的first来计算,当结点数据类型是Key时,则不进行操作
unordered_set
bool insert(const K& key)
{
return _ht.Insert(key);
}
unordered_map
bool insert(const pair<K, V>& kv)
{
return _ht.Insert(kv);
}
3、迭代器
3.1 operator++
unordered_set和unordered_map都是单向迭代器,所以只有operator++,没有operator--
我们先来了解一下