unorderset和unordermap是C++11中心引入的一种关联式容器。set和map与他们之间的不同之处就是遍历时是否有序,通过名字就可以看出,unorderset和unordermap在遍历时是无序的。之所他们有这样的差异,是因为他们底层的实现的数据结构是不同的,unorderset和unordermap底层是通过哈希表来实现的,而set和map底层是通过红黑树来实现的。unorderset和unordermap的插入和查找的效率非常高,这也会引入他们的原因之一。下面我们一起来看看如何实现的
看该文时,你有必要先了解哈希
数据结构 5分钟带你搞定哈希表(建议收藏)!!!
如果你知道如何实现哈希表后,其实unorderset的实现也实现一半了,当然,这里只是简单的实现,和STL库的底层实现还是有一点差别的。首先我们必须要对哈希表进行封装,这里我就直接提供代码,代码和上一篇文章代码类似,只是加了模板和伪函数,该伪函数就是通过val来获取key,用key来作为比较来存储
哈希表中主要成员是指针数组,我们要通过封装该指针数组,来实现迭代器。我们先明白begin()和end()的位置。begin()应该要是第一个非空链表的头结点,而end()应该最后一个节点的下一个位置,这里则指的是nullptr
迭代器最难实现的就是迭代器的++操作,应为是链表,我们不能单词的进行++操作,也就是通过封装结点后,然后对++操作符进行重载来实现的。实现++操作时,有两种可能,第一种是存在next结点,第二种是不存在next结点,存在next结点则让结点直接置为next结点,不存在则需要利用哈希表中的指针数组来获得当前结点的哈希位置,然后去查找下一个非空的链表头结点,找到则将结点置为所找到的头结点,找不到则返回空,表示结束。由于我们用到了哈希表中的私有成员,我们需要将迭代器类设置为哈希表类的友元类。
#include
#include
using namespace std;
template<class K, class V, class KeyOfValue>
class HTable;//声明
template<class V>
struct HashNode
{
typedef HashNode<V> Node;
V _val;
Node* _next;
HashNode(const V& val)
:_val(val)
,_next(nullptr)
{
}
};
template<class K, class V, class KeyOfValue>
struct HashIterator
{
typedef HashNode<V> Node;
typedef HashIterator<K, V, KeyOfValue> Self;
typedef HTable<K, V, KeyOfValue> HT;
Node* _node;
HT* _hPtr;
HashIterator(Node* node, HT* hPtr)
:_node(node)
,_hPtr(hPtr)
{
}
V& operator*()
{
return _node->_val;
}
V* operator->()
{
return &_node->_val;
}
bool operator!=(const Self& it)
{
return _node != it._node;
}
Self& operator++()
{
//下一个结点存在
if (_node->_next)
{
_node = _node->_next;
}
else//不存在下一个节点
{
//则需要找到下一个非空的链表的头结点
//计算当前节点在哈希表中的位置
KeyOfValue kov;
size_t idx = kov(_node->_val) % _hPtr->_ht.size();
//从下一个哈希位置开始查找
++idx;
for (; idx < _hPtr->_ht.size(); ++idx)
{
if (_hPtr->_ht[idx])//找到下一个非空结点
{
_node = _hPtr->_ht[idx];
break;
}
}
//没找到非空链表,则表示走到末尾,则为空
if (idx == _hPtr->_ht.size())
_node = nullptr;
}
return *this;
}
};
template<class K, class V, class KeyOfValue>
class HTable
{
public:
typedef HashIterator<K, V, KeyOfValue> iterator;
typedef HashNode<V> Node;
template<class K, class V, class KeyOfValue>
friend struct HashIterator;
HTable(int n = 10)
:_ht(n)
,_size(0)
{
}
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);
}
pair<iterator, bool> insert(const V& val)
{
//0.检查容量
checkCapacity();
//1.计算hash位置
KeyOfValue kov;
int idx = kov(val) % _ht.size();
//2.查找
Node* cur = _ht[idx];
while (cur)
{
if (kov(cur->_val) == kov(val))
return make_pair(iterator(cur, this), false);
cur = cur->_next;
}
//3.插入--头插
cur = new Node(val);
cur->_next = _ht[idx];
_ht[idx] = cur;
++_size;
return make_pair(iterator(cur, this), true);
}
void checkCapacity()
{
if (_size == _ht.size())
{
int newC = _size == 0 ? 10 : 2 * _size;
//创建新的指针数组
vector<Node*> newHt(newC);
KeyOfValue kov;
//遍历旧表
for (size_t i = 0; i < _ht.size(); ++i)
{
Node* cur = _ht[i];
//遍历单链表
while (cur)
{
Node* next = cur->_next;
//计算新的位置
int idx = kov(cur->_val) % newHt.size();
//头插
cur->_next = newHt[idx];
newHt[idx] = cur;
cur = next;
}
//旧表指针置空
_ht[i] = nullptr;
}
//交换新表和旧表
swap(_ht, newHt);
}
}
private:
//指针数组
vector<Node*> _ht;
int _size;
};
template<class K>
class UnorderedSet
{
struct SetKeyOfValue
{
const K& operator()(const K& key)
{
return key;
}
};
public:
typedef typename HTable<K, K, SetKeyOfValue>::iterator iterator;
pair<iterator, bool> insert(const K& key)
{
return _ht.insert(key);
}
iterator begin()
{
return _ht.begin();
}
iterator end()
{
return _ht.end();
}
private:
HTable<K, K, SetKeyOfValue> _ht;
};
template<class K, class V>
class UnorderedMap
{
struct MapKeyOfValue
{
const K& operator()(const pair<K, V>& val)
{
return val.first;
}
};
public:
typedef typename HTable<K, pair<K, V>, MapKeyOfValue>::iterator iterator;
pair<iterator, bool> insert(const pair<K, V>& val)
{
return _ht.insert(val);
}
iterator begin()
{
return _ht.begin();
}
iterator end()
{
return _ht.end();
}
V& operator[](const K& key)
{
pair<iterator, bool> ret = _ht.insert(make_pair(key, V()));
return ret.first->second;
}
private:
HTable<K, pair<K, V>, MapKeyOfValue> _ht;
};