使用哈希桶来进一步模拟实现unordered系列的两个容器:unordered_set和unordered_map.
1. 节点模板
unordered_set 是 K 模型的容器 unordered_map 是 KV 模型的容器,所以需要对结点的参数进行改造使这两个容器都能使用。对于unordered_set 来说_data 中存的是K,对于unordered_map来说_data中存的是一个由K和V组成的键值对。
template<class T>
struct HashNode
{
T _data;
HashNode<T>* _next;
HashNode(const T& data)
:_data(data)
, _next(nullptr)
{}
};
2.KeyofT仿函数
插入元素时通过哈希函数计算出对应的哈希地址:但是插入的时候就不能直接用data去进行比较了因为unordered_map的data是键值对,键值对不能比较,需要取出键值对的first。而data既可以是unordered_set的,也可以是unordered_map的,所以我们需要仿函数来实现不同容器所对应的需求。
unordered_map的仿函数:
template<class K,class V,class Hash=HashFunc<K>>
class unordered_map
{
struct MapKeyOfT
{
const K& operator()(const pair<const K, V>& kv)
{
return kv.first;
}
};
}
private:
buckethash::HashTable<K, pair<const K, V>, Hash, MapKeyOfT> _ht;
};
unordered_set 的仿函数:
template<class K,class Hash = HashFunc<K>>
class unordered_set
{
struct SetKeyOfT
{
const K& operator()(const K& key)
{
return key;
}
};
private:
buckethash::HashTable<K, K, Hash, SetKeyOfT> _ht;
};
3.哈希函数
在插入数据时,要根据数据的关键码求出存储位置的对应关系。这里哈希函数采用的方法是除留余数法,但是字符串是没办法取模的,所以string需要特化,让string类型的数据也能对应一个存储位置。符合string类型的优先走string的函数。
template<class K>
struct HashFunc
{
size_t operator()(const K& key)
{
return (size_t)key;
}
};
template<>
struct HashFunc<string> //符合string类型的优先走string类型
{
size_t operator()(const string& key)
{
size_t hash = 0;
for (auto ch : key)
{
hash *= 131;
hash += ch;
}
return hash;
}
};
4.正向迭代器
哈希表的正向迭代器对哈希表指针和结点指针进行了封装,operator++可以访问下一个非空的桶中的元素,所以每个正向迭代器里面存的是哈希表地址。
template<class K, class T, class Hash, class KeyOfT>
class HashTable;
template<class K,class T,class Hash,class KeyOfT>
struct __HTIterator
{
typedef HashNode<T> Node;
typedef __HTIterator<K, T, Hash, KeyOfT> Self;
typedef HashTable<K, T, Hash, KeyOfT> HT;
Node* _node;
HT* _ht;
__HTIterator(Node*node,HT*ht) //构造迭代器需要知道哈希表和节点的地址
:_node(node)
,_ht(ht)
{}
}
++运算符重载的实现:如果当前的桶还有节点,那么++就是当前桶下一个节点,如果当前元素是所在的桶的最后一个元素,那么++就是下一个非空的桶了。如何寻找下一个非空的桶:首先通过当前节点的值算出当前桶的hashi,然后++hashi就是下一个桶了,如果下一个桶为空桶,那就一直找直到找到下一个非空桶。
T& operator*()
{
return _node->_data;
}
T* operator->()
{
return &_node->_data;
}
bool operator !=(const Self& s) const
{
return _node != s._node;
}
Self& operator++()
{
if (_node->_next) //访问当前桶中的元素
{
_node = _node->_next;
}
else //开始寻找下一个非空桶
{
KeyOfT kot;
Hash hash;
size_t hashi = hash(kot(_node->_data)) % _ht->_tables.size();
++hashi;
while (hashi < _ht->_tables.size())
{
if (_ht->_tables[hashi])//非空桶
{
_node = _ht->_tables[hashi];
break;
}
else
{
++hashi;
}
}
if (hashi == _ht->_tables.size())
{
_node = nullptr;
}
}
return *this;
}
};
5.重载[ ]
要想实现[],我们需要先把Insert的返回值修改成pair<iterator,bool>,最后的返回值也要一起修改
如果key已经存在,插入失败,insert函数返回该key所在位置的迭代器
pair<iterator, bool> Insert(const T&data)
{
KeyOfT kot;
iterator it = Find(kot(data));
if (it != end())
{
return make_pair(it, false);
}
if (_tables.size() == _n)
{
vector<Node*> newTables;
newTables.resize(__stl_next_prime(_tables.size()), nullptr);
for (size_t i = 0; i < _tables.size(); i++)
{
Node* cur = _tables[i];
while (cur)
{
Node* next = cur->_next;
size_t hashi = Hash()(kot(cur->_data))% newTables.size();
cur->_next = newTables[hashi];
newTables[hashi] = cur;
cur = next;
}
_tables[i] = nullptr;
}
_tables.swap(newTables);
}
size_t hashi = Hash()(kot(data)) % _tables.size();
Node* newnode = new Node(data);
newnode->_next = _tables[hashi];
_tables[hashi] = newnode;
++_n;
return make_pair(iterator(newnode,this),true);
}
6.unordered_set
#pragma once
#include "HashTable.h"
namespace cyf
{
template<class K,class Hash = HashFunc<K>>
class unordered_set
{
struct SetKeyOfT
{
const K& operator()(const K& key)
{
return key;
}
};
public:
typedef typename buckethash::HashTable<K, K, Hash, SetKeyOfT>::iterator iterator;
iterator begin()
{
return _ht.begin();
}
iterator end()
{
return _ht.end();
}
pair<iterator, bool> insert(const K& key)
{
return _ht.Insert(key);
}
private:
buckethash::HashTable<K, K, Hash, SetKeyOfT> _ht;
};
}
7.unordered_map
#pragma once
#include "HashTable.h"
namespace cyf
{
template<class K,class V,class Hash=HashFunc<K>>
class unordered_map
{
struct MapKeyOfT
{
const K& operator()(const pair<const K, V>& kv)
{
return kv.first;
}
};
public:
typedef typename buckethash::HashTable<K, pair<const K, V>, Hash, MapKeyOfT>::iterator iterator;
iterator begin()
{
return _ht.begin();
}
iterator end()
{
return _ht.end();
}
pair<iterator, bool> insert(const pair<K, V> data)
{
return _ht.Insert(data);
}
V& operator[](const K& key)
{
pair<iterator, bool> ret = _ht.Insert(make_pair(key, V()));
return ret.first->second;
}
private:
buckethash::HashTable<K, pair<const K, V>, Hash, MapKeyOfT> _ht;
};
}
8.HashTable.h
#pragma once
#include <iostream>
#include <string>
#include <vector>
using namespace std;
template<class K>
struct HashFunc
{
size_t operator()(const K& key)
{
return (size_t)key;
}
};
template<>
struct HashFunc<string>
{
size_t operator()(const string& key)
{
size_t hash = 0;
for (auto ch : key)
{
hash *= 131;
hash += ch;
}
return hash;
}
};
namespace buckethash
{
template<class T>
struct HashNode
{
T _data;
HashNode<T>* _next;
HashNode(const T& data)
:_data(data)
, _next(nullptr)
{}
};
template<class K, class T, class Hash, class KeyOfT>
class HashTable;
template<class K,class T,class Hash,class KeyOfT>
struct __HTIterator
{
typedef HashNode<T> Node;
typedef __HTIterator<K, T, Hash, KeyOfT> Self;
typedef HashTable<K, T, Hash, KeyOfT> HT;
Node* _node;
HT* _ht;
__HTIterator(Node*node,HT*ht)
:_node(node)
,_ht(ht)
{}
T& operator*()
{
return _node->_data;
}
T* operator->()
{
return &_node->_data;
}
bool operator !=(const Self& s) const
{
return _node != s._node;
}
Self& operator++()
{
if (_node->_next)
{
_node = _node->_next;
}
else
{
KeyOfT kot;
Hash hash;
size_t hashi = hash(kot(_node->_data)) % _ht->_tables.size();
++hashi;
while (hashi < _ht->_tables.size())
{
if (_ht->_tables[hashi])
{
_node = _ht->_tables[hashi];
break;
}
else
{
++hashi;
}
}
if (hashi == _ht->_tables.size())
{
_node = nullptr;
}
}
return *this;
}
};
template<class K,class T,class Hash,class KeyOfT>
class HashTable
{
typedef HashNode<T> Node;
template<class K,class T,class Hash,class KeyOfT>
friend struct __HTIterator;
public:
typedef __HTIterator<K, T, Hash, KeyOfT> iterator;
iterator begin()
{
for (size_t i = 0; i < _tables.size(); i++)
{
if (_tables[i])
{
return iterator(_tables[i], this);
}
}
return iterator(nullptr, this);
}
iterator end()
{
return iterator(nullptr, this);
}
HashTable()
:_n(0)
{
_tables.resize(__stl_next_prime(0));
}
~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;
}
}
pair<iterator, bool> Insert(const T&data)
{
KeyOfT kot;
iterator it = Find(kot(data));
if (it != end())
{
return make_pair(it, false);
}
if (_tables.size() == _n)
{
vector<Node*> newTables;
newTables.resize(__stl_next_prime(_tables.size()), nullptr);
for (size_t i = 0; i < _tables.size(); i++)
{
Node* cur = _tables[i];
while (cur)
{
Node* next = cur->_next;
size_t hashi = Hash()(kot(cur->_data))% newTables.size();
cur->_next = newTables[hashi];
newTables[hashi] = cur;
cur = next;
}
_tables[i] = nullptr;
}
_tables.swap(newTables);
}
size_t hashi = Hash()(kot(data)) % _tables.size();
Node* newnode = new Node(data);
newnode->_next = _tables[hashi];
_tables[hashi] = newnode;
++_n;
return make_pair(iterator(newnode,this),true);
}
iterator Find(const K& key)
{
KeyOfT kot;
size_t hashi = Hash()(key) % _tables.size();
Node* cur = _tables[hashi];
while (cur)
{
if (kot(cur->_data) == key)
{
return iterator(cur, this);
}
else
{
cur = cur->_next;
}
}
return end();
}
bool Erase(const K& key)
{
size_t hashi = Hash()(key) % _tables.size();
Node* prev = nullptr;
Node* cur = _tables[hashi];
while (cur)
{
if (cur->_kv.first == key)
{
if (cur == _tables[hashi])
{
_tables[hashi] = cur->_next;
}
else
{
prev->_next = cur->_next;
}
delete cur;
--_n;
return true;
}
else
{
prev = cur;
cur = cur->_next;
}
}
return false;
}
inline unsigned long __stl_next_prime(unsigned long n)
{
static const int __stl_num_primes = 28;
static const unsigned long __stl_prime_list[__stl_num_primes] =
{
53, 97, 193, 389, 769,
1543, 3079, 6151, 12289, 24593,
49157, 98317, 196613, 393241, 786433,
1572869, 3145739, 6291469, 12582917, 25165843,
50331653, 100663319, 201326611, 402653189, 805306457,
1610612741, 3221225473, 4294967291
};
for (int i = 0; i < __stl_num_primes; ++i)
{
if (__stl_prime_list[i] > n)
{
return __stl_prime_list[i];
}
}
return __stl_prime_list[__stl_num_primes - 1];
}
private:
vector<Node*> _tables;
size_t _n = 0;
};
}