1. unordered_set与unordered_map的结构
我们知道STL
中的unordered_set
与unordered_map
底层就是一个开散列的哈希表
1.1 unordered_set的结构
我们知道unordered_set
其实就是K
模型,所以unordered_set
容器对红黑树的封装如下:
template<class k, class Hash = Hashfunc<k>>
class unordered_set
{
struct SetKeyofT
{
const k& operator()(const k& key)
{
return key;
}
};
public:
private:
HashTable<k, const k, SetKeyofT, Hash> _t;
};
由于unordered_set 和 unordered_map 底层都是哈希表,所以我们需要传一个仿函数来方便后期比较key值
1.2 unordered_map的结构
我们也知道unordered_map
其实就是KV
模型,所以unordered_map
容器对红黑树的封装如下:
template<class k, class T, class Hash = Hashfunc<k>>
class unordered_map
{
struct MapKeyofT
{
const k& operator()(const pair<k, T>& key)
{
return key.first;
}
};
public:
private:
HashTable<k, pair<const k, T>, MapKeyofT, Hash> _t;
};
其中为了防止K值
被修改,我们都加上const
修饰。
2. 改造哈希表
2.1. 模板参数列表的改造
- K:关键码类型
- T: 不同容器T的类型不同,如果是unordered_map,T代表一个键值对,如果是unordered_set,T为 K
- KeyofT: 因为T的类型不同,通过value取key的方式就不同,详细见unordered_map/set的实现
- Hash: 哈希函数仿函数对象类型,哈希函数使用除留余数法,需要将Key转换为整形数字才能取模
//哈希表的节点
template<class T>
struct HashNode
{
T _kv;
HashNode<T>* _next;
HashNode(const T& kv)
: _kv(kv)
,_next(nullptr)
{ }
};
template<class k, class T, class KeyofT, class Hash>
class HashTable
2.2. 增加迭代器操作
注意:因为哈希桶在底层是单链表结构,所以哈希桶的迭代器不需要--操作
//为了实现简单,在哈希桶的迭代器类中需要用到 hashtable本身
template<class k, class T, class KeyofT, class Hash>
class HashTable;
template<class k, class T, class ref, class ptr, class KeyofT, class Hash>
class IteratorHash
{
typedef HashNode<T> Node;
typedef IteratorHash<k, T, ref, ptr, KeyofT, Hash> self;
Node* _node;
const HashTable<k, T, KeyofT, Hash>* _tbptr;
public:
IteratorHash(Node* node, const HashTable<k, T, KeyofT, Hash>* tbptr)
:_node(node)
,_tbptr(tbptr)
{ }
ref operator*()
{
return _node->_kv;
}
ptr operator->()
{
return &_node->_kv;
}
bool operator!=(self& s)
{
return s._node != _node;
}
self& operator++()
{
if (_node->_next != nullptr)
{
_node = _node->_next;
}
else
{
KeyofT kot;
Hash ht;
size_t hashi = ht(kot(_node->_kv)) % _tbptr->_tables.size();
hashi++;
while (hashi < _tbptr->_tables.size() && _tbptr->_tables[hashi] == nullptr)
{
hashi++;
}
if (hashi == _tbptr->_tables.size())
{
_node = nullptr;
}
else
{
_node = _tbptr->_tables[hashi];
}
}
return *this;
}
};
Find函数
Iterator Find(const k& key)
{
Hash hot;
KeyofT kot;
int hashi = hot(key) % _tables.size();
Node* root = _tables[hashi];
while (root)
{
if (kot(root->_kv) == key)
{
return Iterator(root, this);
}
root = root->_next;
}
return Iterator(nullptr, this);
}
在哈希表中的哈希函数一般都需要进行取模操作,但是有些自定义类型如string就无法直接进行取模操作,这时我们就需要通过某种方法将string转化为整型,然后再带入哈希函数求对应的下标。但遗憾的是,我们无法找到一种能实现字符串和整型之间一对一转换的方法,因为在计算机中,整型的大小是有限的,比如用无符号整型能存储的最大数字是4294967295,但是不同字符串能组合的数字是无限的,以无限对有限,这就意味着无论哪种哈希函数都可能存在哈希冲突。
所以我们可以针对string类型写一个特化版本。
template<class k>
struct Hashfunc
{
size_t operator()(const k& _kv)
{
return (size_t)_kv;
}
};
template<>
struct Hashfunc<string>
{
size_t operator()(const string& _kv)
{
size_t a = 0;
for (auto e : _kv)
{
a += e;
a *= 31;
}
return a;
}
};
template<class k, class Hash = Hashfunc<k>>
class unordered_...
{
};
Insert函数
pair<Iterator, bool> Insert(const T& kv)
{
Hash hot;
KeyofT kot;
if (_n == _tables.size())
{
vector<Node*> _newtables(_tables.size() * 2, nullptr);
for (size_t i = 0; i < _tables.size(); i++)
{
while(_tables[i])
{
Node* root = _tables[i]->_next;
int hashi = hot(kot(_tables[i]->_kv)) % (_tables.size()*2);
_tables[i]->_next = _newtables[hashi];
_newtables[hashi] = _tables[i];
_tables[i] = root;
}
}
_tables.swap(_newtables);
}
int hashi = hot(kot(kv)) % _tables.size();
Node* root = _tables[hashi];
_tables[hashi] = new Node(kv);
_tables[hashi]->_next = root;
_n++;
return { Iterator(_tables[hashi], this), true};
}
Erase函数
bool Erase(const k& key)
{
Hash hot;
KeyofT kot;
int hashi = hot(key) % _tables.size();
Node* root = _tables[hashi];
Node* prev = nullptr;
while (root)
{
if (kot(root->_kv) == key)
{
if (prev == nullptr)
{
_tables[hashi] = root->_next;
}
else
{
prev->_next = root->_next;
}
delete root;
_n--;
return true;
}
prev = root;
root = root->_next;
}
return false;
}
3.整体代码如下
Hashtable:
template<class k>
struct Hashfunc
{
size_t operator()(const k& _kv)
{
return (size_t)_kv;
}
};
template<>
struct Hashfunc<string>
{
size_t operator()(const string& _kv)
{
size_t a = 0;
for (auto e : _kv)
{
a += e;
a *= 31;
}
return a;
}
};
namespace bit
{
template<class T>
struct HashNode
{
T _kv;
HashNode<T>* _next;
HashNode(const T& kv)
: _kv(kv)
,_next(nullptr)
{ }
};
template<class k, class T, class KeyofT, class Hash>
class HashTable;
template<class k, class T, class ref, class ptr, class KeyofT, class Hash>
class IteratorHash
{
typedef HashNode<T> Node;
typedef IteratorHash<k, T, ref, ptr, KeyofT, Hash> self;
Node* _node;
const HashTable<k, T, KeyofT, Hash>* _tbptr;
public:
IteratorHash(Node* node, const HashTable<k, T, KeyofT, Hash>* tbptr)
:_node(node)
,_tbptr(tbptr)
{ }
ref operator*()
{
return _node->_kv;
}
ptr operator->()
{
return &_node->_kv;
}
bool operator!=(self& s)
{
return s._node != _node;
}
self& operator++()
{
if (_node->_next != nullptr)
{
_node = _node->_next;
}
else
{
KeyofT kot;
Hash ht;
size_t hashi = ht(kot(_node->_kv)) % _tbptr->_tables.size();
hashi++;
while (hashi < _tbptr->_tables.size() && _tbptr->_tables[hashi] == nullptr)
{
hashi++;
}
if (hashi == _tbptr->_tables.size())
{
_node = nullptr;
}
else
{
_node = _tbptr->_tables[hashi];
}
}
return *this;
}
};
template<class k, class T, class KeyofT, class Hash>
class HashTable
{
typedef HashNode<T> Node;
public:
template<class k, class T, class ref, class ptr,class KeyofT, class Hash>
friend class IteratorHash;
typedef IteratorHash<k, T, T&, T*, KeyofT, Hash> Iterator;
typedef IteratorHash<k, T, const T&, const T*, KeyofT, Hash> ConstIterator;
HashTable()
{
_tables.resize(10, nullptr);
}
~HashTable()
{
for (size_t i = 0; i < _tables.size(); i++)
{
while (_tables[i])
{
Node* root = _tables[i]->_next;
delete _tables[i];
_tables[i] = root;
}
_tables[i] = nullptr;
}
}
Iterator Begin()
{
if (_n == 0) return Iterator(nullptr, this);
else
{
int i = 0;
while (_tables[i] == nullptr)
{
i++;
}
return Iterator(_tables[i], this);
}
}
ConstIterator Begin()const
{
if (_n == 0) return ConstIterator(nullptr, this);
else
{
int i = 0;
while (_tables[i] == nullptr)
{
i++;
}
return ConstIterator(_tables[i], this);
}
}
Iterator End()
{
return Iterator(nullptr, this);
}
ConstIterator End()const
{
return ConstIterator(nullptr, this);
}
pair<Iterator, bool> Insert(const T& kv)
{
Hash hot;
KeyofT kot;
if (_n == _tables.size())
{
vector<Node*> _newtables(_tables.size() * 2, nullptr);
for (size_t i = 0; i < _tables.size(); i++)
{
while(_tables[i])
{
Node* root = _tables[i]->_next;
int hashi = hot(kot(_tables[i]->_kv)) % (_tables.size()*2);
_tables[i]->_next = _newtables[hashi];
_newtables[hashi] = _tables[i];
_tables[i] = root;
}
}
_tables.swap(_newtables);
}
int hashi = hot(kot(kv)) % _tables.size();
Node* root = _tables[hashi];
_tables[hashi] = new Node(kv);
_tables[hashi]->_next = root;
_n++;
return { Iterator(_tables[hashi], this), true};
}
Iterator Find(const k& key)
{
Hash hot;
KeyofT kot;
int hashi = hot(key) % _tables.size();
Node* root = _tables[hashi];
while (root)
{
if (kot(root->_kv) == key)
{
return Iterator(root, this);
}
root = root->_next;
}
return Iterator(nullptr, this);
}
bool Erase(const k& key)
{
Hash hot;
KeyofT kot;
int hashi = hot(key) % _tables.size();
Node* root = _tables[hashi];
Node* prev = nullptr;
while (root)
{
if (kot(root->_kv) == key)
{
if (prev == nullptr)
{
_tables[hashi] = root->_next;
}
else
{
prev->_next = root->_next;
}
delete root;
_n--;
return true;
}
prev = root;
root = root->_next;
}
return false;
}
private:
vector<Node*> _tables;
size_t _n = 0;
};
}
unordered_set:
#include"hashtable.h"
template<class k, class Hash = Hashfunc<k>>
class unordered_set
{
struct SetKeyofT
{
const k& operator()(const k& key)
{
return key;
}
};
public:
typedef typename HashTable<k, const k, SetKeyofT, Hash>::Iterator iterator;
typedef typename HashTable<k, const k, SetKeyofT, Hash>::ConstIterator const_iterator;
iterator begin()
{
return _t.Begin();
}
const_iterator begin()const
{
return _t.Begin();
}
iterator end()
{
return _t.End();
}
const_iterator end()const
{
return _t.End();
}
pair<iterator, bool> insert(const k& key)
{
return _t.Insert(key);
}
iterator find(const k& key)
{
_t.Find(key);
}
bool erase(const k& key)
{
return _t.Erase(key);
}
private:
HashTable<k, const k, SetKeyofT, Hash> _t;
};
unordered_map:
#include"hashtable.h"
namespace bit
{
template<class k, class T, class Hash = Hashfunc<k>>
class unordered_map
{
struct MapKeyofT
{
const k& operator()(const pair<k, T>& key)
{
return key.first;
}
};
public:
typedef typename HashTable<k, pair<const k, T>, MapKeyofT, Hash>::Iterator iterator;
typedef typename HashTable<k, pair<const k, T>, MapKeyofT, Hash>::ConstIterator const_iterator;
iterator begin()
{
return _t.Begin();
}
const_iterator begin()const
{
return _t.Begin();
}
iterator end()
{
return _t.End();
}
const_iterator end()const
{
return _t.End();
}
pair<iterator, bool> insert(const pair<k, T>& kv)
{
return _t.Insert(kv);
}
T& operator[](const k& k)
{
return (*(insert({ k, T() }).first)).second;
}
iterator find(const k& k)
{
return _t.Find(k);
}
bool erase(const k& k)
{
return _t.Erase(k);
}
private:
HashTable<k, pair<const k, T>, MapKeyofT, Hash> _t;
};
感谢大家的观看!