unordered_map与unordered_set的实现

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;
};

对哈希表的改造

  1. 哈希表的结点定义是只需要一个模板用来表示数据类型, 因为是 K 还是 pair<K,V>后面的仿函数会进行判断

  2. 表中的几个接口再计算索引的时候需要对节点中的数据进行操控, 所以需要给出仿函数对象,在用重载的 () 进行计算.

  3. 因为开散列迭代器中的成员有哈希表的表头指针, 而且哈希表中也要对迭代器的 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;
};
unordered_map与unordered_set有什么区别? 回答: unordered_map和unordered_set都是关联式容器,类似于键值对 (key-value) 的模型。它们的底层实现方式不同,unordered_map使用哈希表作为底层数据结构,而unordered_set也是使用哈希表。unordered_map和unordered_set的区别在于它们存储的类型不同,即unordered_map存储键值对,而unordered_set存储单个元素。此外,unordered_map和unordered_set在功能上也有一些区别。unordered_map提供了以键为索引的查找功能,而unordered_set则提供了判断元素是否存在的功能。从效率上来看,unordered_map和unordered_set的增删查改操作的时间复杂度都是O(1),即常数时间。而mapset的时间复杂度为O(logN),其中N是容器中的元素数量。所以在对效率要求较高的情况下,选择unordered_map和unordered_set会更合适。但是,unordered_map和unordered_set相比于mapset会消耗更多的内存空间。因此,在对数据有排序或者对空间有要求的情况下,选择mapset;而对于对效率有要求的情况,选择unordered_map和unordered_set更合适。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [STL详解(十二)—— unordered_set、unordered_map的介绍及使用](https://blog.csdn.net/chenlong_cxy/article/details/122277348)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [模拟实现unordered_map和unordered_set超详解(C++)](https://blog.csdn.net/m0_67430750/article/details/124760725)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [unordered_set和unordered_map的使用【STL】](https://blog.csdn.net/m0_63312733/article/details/128000844)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值