文章目录
一、改造哈希表
由于哈希桶解决冲突的方式更加有效且简单,所以这里底层哈希表采用拉链法。
1.1 哈希表的数据结构
为了让unordered系列容器使用同一个哈希表,需要修改哈希结点的定义,变得更加抽象
template<class T>
struct HashNode
{
T _data;
HashNode<T>* _next;
HashData(const T& data)
:_data(data), _next(nullptr)
{}
};
此外还要增加一个仿函数获得不同类型的key值。所以现在的哈希表的定义为:
template<class K, class V, class KeyOfT, class HashFunc >
class HashTable
{
typedef HashNode<K, V> Node;
private:
vector<Node*> _tables;
size_t _n = 0; // 有效数字个数
}
1.2 哈希表的迭代器
哈希表的迭代器怎么设计呢?
观察unordered_map的函数可以知道,它的迭代器是一个单向的,也就是只有 ++ 。哈希表的迭代器需要从某一个位置开始遍历它所在的桶元素,然后遍历后面的桶,为了方便,可以将哈希表对象的传给迭代器,迭代器就能根据当前的key值确定位置,然后开始往后遍历。
迭代器的结构如下
// 迭代器里面会用到,所以提前声明
template<class K, class T, class KeyOfT, class HashFunc >
class HashTable;
template<class K,class T, class Ref, class Ptr, class KeyOfT, class HashFunc>
struct HTableIterator
{
typedef HashNode<T> Node;
typedef HTableIterator<K, T, Ref, Ptr, KeyOfT, HashFunc> Self;
Node* _node;
HashTable<K, T, KeyOfT, HashFunc>* _pht; // 存放哈希表的地址
HTableIterator(Node* node, HashTable<K, T, KeyOfT, HashFunc>* pht)
:_node(node), _pht(pht)
{}
};
- 迭代器寻找后一个
这里需要使用哈希表的私有元素,所以可以将迭代器声明为哈希表的友元类,或者创建get方法。
Self& operator++()
{
// 如果当前桶下面还有数据,直接输出
// 否则寻找下一个不为空的桶
if (_node->_next != nullptr)
{
_node = _node->_next;
}
else
{
KeyOfT kot;
HashFunc hf;
// 重新映射,找到下标
size_t index = hf(kot(_node->_data)) % _pht->_tables.size();
++index;
while (index < _pht->_tables.size())
{
if (_pht->_tables[index] != nullptr)
{
break;
}
else
{
++index;
}
}
// 表后面都找完了没有找到,返回空
if (index == _pht->_tables.size())
{
_node = nullptr;
}
else
{
_node = _pht->_tables[index];
}
}
return *this;
}
- 其他运算符重载
Ref operator*()
{
return _node->_data;
}
Ptr operator->()
{
return &_node->_data;
}
bool operator!=(const Self& s)
{
return _node != s._node;
}
bool operator==(const Self& s)
{
return _node == s._node;
}
1.3 哈希表迭代器相关函数
一些typedef
// 将迭代器声明为友元类这样就能访问哈希表的私有元素
template<class K, class T, class Ref, class Ptr, class KeyOfT, class HashFunc>
friend struct HTableIterator;
typedef HashNode<T> Node;
typedef HTableIterator <K, T, T&, T*, KeyOfT, HashFunc> iterator;
typedef HashTable<K, T, KeyOfT, HashFunc> Self;
1.3.1 类的默认函数
// C++ 11 ,显示指定编译器去生成默认构造函数
HashTable() = default;
// 拷贝构造函数
HashTable(const Self & s)
{
_tables.resize(s._tables.size());
for (int i = 0; i < s._tables.size(); i++)
{
Node* cur = s._tables[i];
while (cur)
{
Node* copy = new Node(cur->_data);
copy->_next = _tables[i];
_tables[i] = copy;
++_n;
cur = cur->_next;
}
}
}
// 赋值运算重载
Self& operator=(Self copy)
{
_tables.swap(copy._tables);
_n = copy._n;
return *this;
}
~HashTable()
{
for (int i = 0; i < _tables.size(); i++)
{
Node* cur = _tables[i];
while (cur)
{
Node* next = cur->_next;
delete next;
--_n;
cur = next;
}
_tables[i] = nullptr;
}
}
1.3.2 迭代器相关函数
iterator begin()
{
// 寻找第一个有元素的桶
for (int i = 0; i < _tables.size(); i++)
{
if (_tables[i] != nullptr)
{
return iterator(_tables[i], this);
}
}
// 没有
return end();
}
iterator end()
{
return iterator(nullptr, *this);
}
同时也要改变Find的返回值类型为迭代器
插入函数的返回值类型为一个元组pair<iterator, bool>
方便上层实现[]
重载
二、封装unordered_map
这下封装就很简单了,只需要调用哈希表的函数就好了
#pragma once
namespace sqin
{
template<class K, class V,class HashFunc = Hash<K>>
class unordered_map
{
struct MapOfT
{
const K& operator()(const pair<K, V>& kv)
{
return kv.first;
}
};
private:
HashTable<K, pair<K, V>, MapOfT, HashFunc> _ht;
public:
typedef typename HashTable<K, pair<K, V>, MapOfT, HashFunc>::iterator iterator;
iterator begin()
{
return _ht.begin();
}
iterator end()
{
return _ht.end();
}
V& operator[](const K& key)
{
auto res = _ht.Insert(make_pair(key, V()));
return res.first->second;
}
pair<iterator, bool> insert(const pair<K, V>& kv)
{
return _ht.Insert(kv);
}
};
}
三、封装unordered_set
#pragma once
namespace sqin
{
template<class K, class HashFunc = Hash<K>>
class unordered_set
{
struct SetOfT
{
const K& operator()(const K& key)
{
return key;
}
};
private:
HashTable<K, K, SetOfT, HashFunc> _ht;
public:
typedef typename HashTable<K, K, SetOfT, HashFunc>::iterator iterator;
iterator begin()
{
return _ht.begin();
}
iterator end()
{
return _ht.end();
}
pair<iterator, bool> insert(const K& key)
{
return _ht.Insert(key);
}
};
void test_unordered_set()
{
unordered_set<int> us;
us.insert(5);
us.insert(15);
us.insert(35);
us.insert(8);
us.insert(24);
us.insert(18);
unordered_set<int>::iterator it = us.begin();
while (it != us.end())
{
cout << *it << " ";
++it;
}
cout<<endl;
unordered_set<int> copyus(us);
for (auto e : copyus)
{
cout << e << " ";
}
unordered_set<string> uss;
uss.insert("aaa");
uss.insert("bbb");
for (auto e : uss)
{
cout << e << " ";
}
unordered_set<int> us2;
for (size_t i = 0; i < 100; ++i)
{
us2.insert(i);
}
for (auto e : us2)
{
cout << e << " ";
}
}
}