unordered_map与unordered_set
unordered_map与unordered_set大体上与map和set的特性是相同的, 但是unordered系列的关联式容器是无顺序的, 这是因为他的底层结构为哈希表,而且由于哈希表的性能略高于红黑树, 因此unordered关联式容器的性能也略高
除此之外, map 与 set 的迭代器因为红黑树每个节点是三叉的,所以它提供双向的迭代器, 而unordered系列则值提供单向的迭代器.
下文中将unordered_map表示为Umap,将unordered_set表示为Uset;
由于Umap的键值是 K 数据是 pair<K,V>的键值对,而 Uset 的键值与数据都是K值,因此为了用同一张哈希表实现 Umap 与 Uset 我们就要在提供一个仿函数, 用来判断数据类型;
template<class K,class V>
class UnorderedMap{
struct UMapKeyOfValue{
const K& operator()(const pair<K, V>& data){
return data.first;
}
};
public:
typedef typename HashTable<K, pair<K, V>, UMapKeyOfValue>::iterator iterator;
iterator begin(){
return _mht.begin();
}
iterator end(){
return _mht.end();
}
bool Insert(const pair<K,V>& data){
return _mht.Insert(data);
}
private:
HashTable<K, pair<K, V>, UMapKeyOfValue> _mht;
};
template<class K>
class UnorderedSet{
struct USetKeyOfValue{
const K& operator()(const K& data){
return data;
}
};
public:
typedef typename HashTable<K, K, USetKeyOfValue>::iterator iterator;
iterator begin(){
return _sht.begin();
}
iterator end(){
return _sht.end();
}
bool Insert(const K& key){
return _sht.Insert(key);
}
private:
HashTable<K, K, USetKeyOfValue> _sht;
};
对哈希表的改造
-
哈希表的结点定义是只需要一个模板用来表示数据类型, 因为是 K 还是 pair<K,V>后面的仿函数会进行判断
-
表中的几个接口再计算索引的时候需要对节点中的数据进行操控, 所以需要给出仿函数对象,在用重载的 () 进行计算.
-
因为开散列迭代器中的成员有哈希表的表头指针, 而且哈希表中也要对迭代器的 begin() 和 end() 进行封装, 多以当迭代器定义在哈希表的后面是, 要将迭代器写成哈希表的友元类, 并且在哈希表的前面进行类型声明…反之,将迭代器定义在哈希表的签名是, 也要对哈希表进行相同操作.
改造后哈希表
template<class V>
struct HashNode{
HashNode(const V& data = V())
:_data(data)
, _next(nullptr)
{}
V _data;
HashNode<V>* _next;
};
//迭代器的前置声明
template<class K,class V,class KeyOfValue>
struct _HashIterator;
template<class K, class V,class KeyOfValue>
class HashTable{
public:
typedef HashNode<V> Node;
typedef Node* pNode;
typedef _HashIterator<K, V,KeyOfValue> iterator;
//模板友元类的声明
template<class K,class V,class KeyOfValue>
friend struct _HashIterator;
iterator begin(){
for (size_t i = 0; i < _ht.size(); i++){
if (_ht[i])
return iterator(_ht[i],this);
}
return iterator(nullptr, this);
}
iterator end(){
return iterator(nullptr, this);
}
HashTable(size_t n = 10){
_ht.resize(10);
_size = 0;
}
//插入
bool Insert(const V& data){
HashCapacity();
KeyOfValue kov;
//计算哈希桶的位置
int index = kov(data) % _ht.size();
pNode cur = _ht[index];
//判断哈希桶中是否已经有要插入的元素
while (cur){
if (kov(cur->_data)== kov(data))
return false;
cur = cur->_next;
}
//创建出新节点
cur = new Node(data);
//头插
cur->_next = _ht[index];
_ht[index] = cur;
++_size;
return true;
}
//查找
pNode Find(const K& key){
//计算索引,找到在表桶的位置
int index = key % _ht.size();
pNode cur = _ht[index];
while (cur){
if (kov(cur->_data) == key)
return cur;
cur = cur->_next;
}
return nullptr;
}
//删除
bool Erase(const K& key){
int index = key%_ht.size();
pNode cur = _ht[index];
pNode prev = nullptr;
while (cur){
if (kov(cur->_data) == key){
if (prev == nullptr)
_ht[index] = nullptr;
else
prev->_next = cur->_next;
delete cur;
cur = nullptr;
return true;
}
prev = cur;
cur = cur->_next;
}
return false;
}
private:
//哈希桶的增容
void HashCapacity(){
//计算是否需要增容
KeyOfValue kov;
if (_size == _ht.size()){
int newC = _size == 0 ? 10 : 2 * _ht.size();
//创建出新表
vector<pNode> newT;
newT.resize(newC);
//将旧表中的所有元素放入到新表中
for (size_t i = 0; i < _ht.size(); i++){
pNode cur = _ht[i];
//遍历一个桶中的左右节点
while (cur){
pNode next = cur->_next;
int index = kov(cur->_data) % newT.size();
cur->_next = newT[index];
newT[index] = cur;
cur = next;
}
_ht[i] = nullptr;
}
swap(_ht, newT);
}
}
//哈希表的表头存放链表的头
vector<pNode> _ht;
size_t _size;
};
改造后的迭代器
template<class K, class V,class KeyOfValue>
struct _HashIterator{
public:
typedef HashNode<V> Node;
typedef Node* pNode;
typedef HashTable<K, V,KeyOfValue> HTable;
typedef _HashIterator<K, V,KeyOfValue> Self;
_HashIterator(pNode node,HTable* ptr)
:_node(node)
, _ptr(ptr)
{}
V& operator*(){
return _node->_data;
}
V* operator->(){
return &_node->_data;
}
Self& operator++(){
KeyOfValue kov;
//若当前节点的下一个存在,则直接走向下一个
if (_node->_next != nullptr)
_node = _node->_next;
else{
//计算出,当前哈希桶的位置,找到下一个头不为空的位置
int index = kov(_node->_data)%_ptr->_ht.size();
++index;
while (index < _ptr->_ht.size()){
//若当前桶有元素,则下一个就在此
if (_ptr->_ht[index]){
_node = _ptr->_ht[index];
break;
}
++index;
}
//若走到最好,则下一个为空
if (index == _ptr->_ht.size())
_node = nullptr;
}
return *this;
}
bool operator!=(const Self& it){
return _node != it._node;
}
private:
pNode _node;
HTable* _ptr;
};