【C++ 第十七章】封装 unordered_map / unordered_set

[复制链接]
发表于 2026-1-31 12:15:34 | 显示全部楼层 |阅读模式



声明:上一章讲授了 哈希结构的原理与实现,本章告急将上一章学过的拉链法的哈希结构封装进 unordered_map / unordered_set,以是须要先学过相干知识,才气更好吸取本章知识
上一章的链接:【C++ 第十六章】哈希




1. unordered_map / unordered_set 的 根本结构

之前封装 map 和 set 时,由于 map 的数据范例为 pair<key, value>,set 的数据范例为 key
则底层红黑树节点的数据范例为 T:T 可以是 pair<key, value>,也可以是 key,以此同时适配 map 和 set
在比力步调中,须要将节点数据的 key 和 其他节点的 key 比力,data < key
当 T 为 key 时,变量 T data,data 就是 key 范例数据:可以 data < key
当 T 为 pair<key, value> 时,变量 T data,data.first 才是 key 范例数据:不可以直接 data < key
因此须要各安闲 map 和 set 结构中,操持仿函数 KeyOfT

(1) unordered_set 的 根本结构

  1. namespace my
  2. {
  3.         template<class K>
  4.         class unordered_set
  5.         {
  6.                 // 仿函数:将 data 转换成 key
  7.                 struct set_KeyOfT {
  8.                         const K& operator()(const K& key) {
  9.                                 return key;
  10.                         }
  11.                 };
  12.         public:
  13.        
  14.         private:
  15.                 hash_bucket::HashTable<K, K, set_KeyOfT> _ht;   // 直接封装一个哈希结构
  16.         };
  17. }
复制代码

(2) unordered_map 的 根本结构

  1. namespace my
  2. {
  3.         template<class K, class V>
  4.         class unordered_map
  5.         {
  6.                 // 仿函数:将 data 转换成 key
  7.                 struct map_KeyOfT
  8.                 {
  9.                         const K& operator()(const pair<K, V>& kv) {
  10.                                 return kv.first;
  11.                         }
  12.                 };
  13.         public:
  14.         private:
  15.                 // 注意要加上类域指定,否则编译器报奇怪的错误
  16.                 hash_bucket::HashTable<K, pair<K, V>, map_KeyOfT> _ht; m  // 直接封装一个哈希结构
  17.         };
  18. }
复制代码

2. 操持哈希表迭代器

由于我们哈希表利用 拉链法的结构,因此迭代器实际上是封装链表节点指针

2.1 前置++ 功能

实现思绪:哈希表迭代器 ++,即从当前有用数据节点到下一个有用数据节点,这有两种情况,一是当前链表节点不是尾节点,next 即为下一个有用数据节点;二是当前节点为尾节点,next为空,则须要今后遍历,跳到下一个单链表,继续探求有用数据节点
怎样从当前链表尾节点,到下一条链表?




我们这里接纳的方法:将 “哈希表” 传入迭代器类中,通过 除留余数法 获取当前哈希桶位置 hashi(这就是哈希表数组下标),hashi ++ 就可以直接跳到下一个哈希桶中了,

代码:讲授思绪

  1. if (下一个节点不为空) {
  2.         直接到下一个节点
  3. }
  4. else if (下一个节点为空) {
  5.         除留余数法 获取当前哈希桶位置 hashi
  6.         hashi++  跳到下一个桶
  7.         while (hashi < 哈希表的size) {
  8.                 如果下一个桶是空的,就需要一直循环,直到找到非空桶
  9.         }
  10.         if (hashi >= 哈希表的size:说明到最后都没有非空桶) {
  11.                 指针直接指向 nullptr
  12.         }
  13.         else if(找到非空桶){
  14.                 指针指向新链表
  15.         }
  16. }
复制代码

实际代码

  1. // HT 是 哈希表指针
  2. Self& operator++() {
  3.         assert(_pNode);
  4.         // 逻辑是 ++ 到下一个有效元素位置:因此需要判断下一个位置是否是空节点
  5.         if (_pNode->_next != nullptr) {
  6.                 _pNode = _pNode->_next;
  7.         }
  8.         else {
  9.                 // 因为一个链表遍历完,需要重新到另一个哈希桶,因此还需要重新更新迭代器指向,需要获取哈希表信息,则传一个哈希表指针过来
  10.                 KeyOfT kot;
  11.                 Hash hash;
  12.                 size_t hashi = hash(kot(_pNode->_data)) % (HT->_table.size());
  13.                 hashi++;  // 这样就跳到下一个桶了
  14.                 // 如果下一个桶是空的,就继续跳
  15.                 while (hashi < (HT->_table.size())) {
  16.                         if (HT->_table[hashi]) break;
  17.                         hashi++;  // 这样就跳到下一个桶了
  18.                 }
  19.                 if (hashi >= (HT->_table.size())) {
  20.                         _pNode = nullptr;
  21.                 }
  22.                 else {
  23.                         _pNode = HT->_table[hashi];
  24.                 }
  25.         }
  26.         return *this;
  27. }
复制代码


2.2 迭代器 完备代码

一些根本的功能接口这里就不赘诉,告急是明确 前置++ 函数
  1. // 前置声明,迭代器类中使用哈希表,程序会向上查找是否有哈希表代码实现,否则识别不了,需要提前声明
  2. template<class K, class T, class KeyOfT, class Hash>
  3. class HashTable;
  4. // 迭代器
  5. template <class K, class T, class Ref, class Ptr, class KeyOfT, class Hash = HashFunc<K>>
  6. class HashIterator
  7. {
  8.         typedef HashNode<T> Node;
  9.         typedef HashIterator<K, T, Ref, Ptr, KeyOfT, Hash> Self;
  10. public:
  11.         Node* _pNode;   // 链表节点指针
  12.         HashTable<K, T, KeyOfT, Hash>* HT; // 获取哈希表指针
  13.        
  14.         // 基本的功能接口
  15.         Ref operator*() {
  16.                 return _pNode->_data;
  17.         }
  18.         Ptr operator->() {
  19.                 return &(_pNode->_data);
  20.         }
  21.         bool operator!=(const Self& it) {
  22.                 return _pNode != it._pNode;
  23.         }
  24.         bool operator==(const Self& it) {
  25.                 return _pNode == it._pNode;
  26.         }
  27.         // 前置++
  28.         Self& operator++() {
  29.                 assert(_pNode);
  30.                 // 逻辑是 ++ 到下一个有效元素位置:因此需要判断下一个位置是否是空节点
  31.                 // 因为一个链表遍历完,需要重新到另一个哈希桶,因此还需要重新更新迭代器指向,需要获取哈希表信息,则传一个哈希表指针过来
  32.                 if (_pNode->_next != nullptr) {
  33.                         _pNode = _pNode->_next;
  34.                 }
  35.                 else {
  36.                         KeyOfT kot;
  37.                         Hash hash;
  38.                         size_t hashi = hash(kot(_pNode->_data)) % (HT->_table.size());
  39.                         hashi++;  // 这样就跳到下一个桶了
  40.                         // 如果下一个桶是空的,就继续跳
  41.                         while (hashi < (HT->_table.size())) {
  42.                                 if (HT->_table[hashi]) break;
  43.                                 hashi++;  // 这样就跳到下一个桶了
  44.                         }
  45.                         if (hashi >= (HT->_table.size())) {
  46.                                 _pNode = nullptr;
  47.                         }
  48.                         else {
  49.                                 _pNode = HT->_table[hashi];
  50.                         }
  51.                 }
  52.                 return *this;
  53.         }
  54.         HashIterator(Node* pNode, HashTable<K, T, KeyOfT, Hash>* pHT)
  55.                 :_pNode(pNode)
  56.                 , HT(pHT)
  57.         {}
  58. };
复制代码


3. 在哈希表中封装与应用迭代器


  1. template<class K, class T, class KeyOfT, class Hash>
  2. class HashTable
  3. {
  4.         // 将迭代器类设置成 友元,以便迭代器类可以访问该类的私有:迭代器类的 operator++ 函数实现中,需要访问哈希表类的 私有成员_table
  5.         template <class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>
  6.         friend class HashIterator;
  7.         typedef HashNode<T> Node;
  8. public:
  9.     // 定义迭代器类型 Iterator 和  Const_Iterator
  10.         typedef HashIterator<K, T, T&, T*, KeyOfT, Hash> Iterator;
  11.         typedef HashIterator<K, T, const T&, const T*, KeyOfT, Hash> Const_Iterator;
  12.         // 迭代器
  13.         Iterator begin() {
  14.                 // 先找到有效数据节点:不是每个哈希桶都有数据的
  15.                 for (size_t i = 0; i < _table.size(); ++i) {
  16.                         if (_table[i]) return Iterator(_table[i], this);
  17.                 }
  18.         }
  19.         Iterator end() {
  20.                 return Iterator(nullptr, this);
  21.         }
  22.         Const_Iterator begin() const {
  23.                 // 先找到
  24.                 for (size_t i = 0; i < _table.size(); ++i) {
  25.                         if (_table[i]) return Const_Iterator(_table[i], this);
  26.                 }
  27.         }
  28.         Const_Iterator end() const {
  29.                 return Const_Iterator(nullptr, this);
  30.         }
  31.         // ..... 其他函数
  32. private:
  33.         //也可以使用 list:双向带头循环链表
  34.         //vector<list<pair<K, V>>> _table;  
  35.         vector<Node*> _table;
  36.         size_t _n = 0; // 负载因子
  37.         Hash hash;
  38.         KeyOfT kot;
  39. };
复制代码


参考STL库的写法,以及为了后续实现 operator[] ,将 find 函数的返回值改成 Iterator,将 insert 函数的返回值改成 pair<Iterator, bool> ,而且两个函数内部一些步调对应轻微修改,这里不赘述

哈希表类完备代码

实现函数:封装迭代器 begin / end 、const_begin / const_end 、插入 insert、删除 erase、查询 find 
  1. template<class K, class T, class KeyOfT, class Hash>
  2. class HashTable
  3. {
  4.         // 将迭代器类设置成 友元,以便迭代器类可以访问该类的私有:迭代器类的 operator++ 函数实现中,需要访问哈希表类的 私有成员_table
  5.         template <class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>
  6.         friend class HashIterator;
  7.         typedef HashNode<T> Node;
  8. public:
  9.         typedef HashIterator<K, T, T&, T*, KeyOfT, Hash> Iterator;
  10.         typedef HashIterator<K, T, const T&, const T*, KeyOfT, Hash> Const_Iterator;
  11.         HashTable() {
  12.                 // 一开始vector里面是随机值,不是 nullptr!! 要自己初始化
  13.                 _table.resize(10, nullptr);
  14.         }
  15.         // 本哈希桶需要显式写析构:因为 Node* 是内置类型,只会默认析构成 nullptr,链表节点不会被处理
  16.         ~HashTable() {
  17.                 Node* cur = nullptr;
  18.                 Node* next = nullptr;
  19.                 for (size_t i = 0; i < _table.size(); ++i) {
  20.                         cur = _table[i];
  21.                         while (cur) {
  22.                                 next = cur->_next;
  23.                                 delete cur;
  24.                                 cur = next;
  25.                         }
  26.                 }
  27.         }
  28.         // 迭代器
  29.         Iterator begin() {
  30.                 // 先找到
  31.                 for (size_t i = 0; i < _table.size(); ++i) {
  32.                         if (_table[i]) return Iterator(_table[i], this);
  33.                 }
  34.         }
  35.         Iterator end() {
  36.                 return Iterator(nullptr, this);
  37.         }
  38.         Const_Iterator begin() const {
  39.                 // 先找到
  40.                 for (size_t i = 0; i < _table.size(); ++i) {
  41.                         if (_table[i]) return Const_Iterator(_table[i], this);
  42.                 }
  43.         }
  44.         Const_Iterator end() const {
  45.                 return Const_Iterator(nullptr, this);
  46.         }
  47.         Iterator find(const K& key) {
  48.                 size_t hashi = hash(key) % _table.size();
  49.                 Node* cur = _table[hashi];
  50.                 while (cur) {
  51.                         if (kot(cur->_data) == key) return Iterator(cur, this);
  52.                         cur = cur->_next;
  53.                 }
  54.                 return end();
  55.         }
  56.         pair<Iterator, bool> insert(const T& data) {
  57.                 // 考虑冗余
  58.                 Iterator ret = find(kot(data));
  59.                 if (ret != end()) {
  60.                         cout << "该数据已存在" << '\n';
  61.                         return make_pair(ret, false);
  62.                 }
  63.                 // 考虑扩容:当负载比率为 1 时,扩容(即 n == size)
  64.                 // 甚至可以大于 1,即理想情况下,就是平均每个桶有 1个以上
  65.                 if (_n == _table.size()) {
  66.                         HashTable<K, T, KeyOfT, Hash> newTable;
  67.                         newTable._table.resize(2 * _table.size());
  68.                         // 若直接遍历每个节点,取数值 data insert 插入新表,会导致频繁的 new 节点,造成一定消耗
  69.                         // 我们可以直接将旧表的节点 直接转接到 新表,省去new节点的消耗
  70.                         for (size_t i = 0; i < _table.size(); ++i) {
  71.                                 Node* cur = _table[i];
  72.                                 /*while (cur) {
  73.                                         newTable.insert(cur->_data);
  74.                                         cur = cur->_next;
  75.                                 }*/
  76.                                 while (cur) {
  77.                                         Node* Next = cur->_next;
  78.                                         size_t hashi = hash(kot(cur->_data)) % newTable._table.size();
  79.                                         cur->_next = newTable._table[hashi];
  80.                                         newTable._table[hashi] = cur;
  81.                                         cur = Next;
  82.                                 }
  83.                                 _table[i] = nullptr;
  84.                         }
  85.                         _table.swap(newTable._table);
  86.                 }
  87.                 size_t hashi = hash(kot(data)) % _table.size();
  88.                 // 头插和尾插都行:头插最方便
  89.                 Node* newNode = new Node(data);
  90.                 newNode->_next = _table[hashi];
  91.                 _table[hashi] = newNode;
  92.                 _n++;
  93.                 return make_pair(Iterator(newNode, this), true);
  94.         }
  95.         bool erase(const K& key) {
  96.                 Iterator ret = find(kot(data));
  97.                 if (ret._pNode == nullptr) {
  98.                         cout << "该节点不存在" << '\n';
  99.                         return false;
  100.                 }
  101.                 // 删除函数需要自己找目标节点,因为底层是单链表,没有prev,删除不好搞
  102.                 size_t hashi = hash(key) % _table.size();
  103.                 Node* cur = _table[hashi];
  104.                 Node* prev = nullptr;
  105.                 while (cur) {
  106.                         if (kot(cur->_data) == key) {
  107.                                 if (prev == nullptr) _table[hashi] = cur->_next;
  108.                                 else prev->_next = cur->_next;
  109.                                 delete cur;
  110.                                 cur = nullptr;
  111.                                 --_n;
  112.                                 return true;
  113.                         }
  114.                         prev = cur;
  115.                         cur = cur->_next;
  116.                 }
  117.                 return false;
  118.         }
  119. private:
  120.         //也可以使用 list:双向带头循环链表
  121.         //vector<list<pair<K, V>>> _table;  
  122.         vector<Node*> _table;
  123.         size_t _n = 0; // 负载因子
  124.         Hash hash;
  125.         KeyOfT kot;
  126. };
复制代码



4. 美满 unordered_map / unordered_set 结构

前面实现了 哈希表迭代器 与 哈希表
因此,在 unordered_map / unordered_set 类中添加这些相干利用以美满。  【迭代器 begin/end、const_begin/const_end、插入insert、删除erase、查询find  】



4.1 u_map 的 operator[] 功能

前面实现了 insert 函数的返回值为 pair<iterator, bool>
(1)若插入乐成,则返回的 pair<iterator, bool> 中的 iterator 迭代器指向 新元素,bool == true
(2)若插入失败,则返回的 pair<iterator, bool> 中的 iterator 迭代器指向 已存在且数值雷同的谁人元素,bool == false

operator[] 返回迭代器 iterator 指向节点中数据 data.second == value
  1. // operator[]
  2. V& operator[](const K& key) {
  3.         pair<iterator, bool> ret = _ht.insert({ key, V() });
  4.         return ret.first->second;
  5. }
复制代码

4.2 关于处理处罚 u_map / u_set 的 key 值不能修改

由于 unordered_map / unordered_set 底层利用 拉链法的哈希结构,须要通过 除留余数法 盘算 key 值映射的下标位置,确定该数据的存放位置
假如修改了 u_map / u_set 的 key,则 删除或查询利用时,通过 除留余数法 盘算 key 值映射的下标位置 就会出现严肃错误!!好比原来 key == 19,映射的位置 hashi == 19/10 == 9,将key修改,key == 23,映射的位置就厘革 hashi == 23/10 == 3,位置就变了!!

操持步调:将参数 key 利用 const 修饰,从源头上限定该范例的参数不能被修改
对于 unordered_set,须要将 哈希表 模板范例参数修改成 const K
  1. HashTable<K, const K, set_KeyOfT, Hash> _ht;
复制代码
对于 unordered_map,须要将 哈希表 模板范例参数修改成 pair<const K, V>  
  1. HashTable<K, pair<const K, V>, map_KeyOfT, Hash> _ht;
复制代码
其他某些部门也要对应修改,自行调解(通常会报错来提示你,假如不改 doge)



4.3 unordered_map 的完备代码

  1. #pragma once#include"HashTable.h"namespace my{        template<class K, class V, class Hash = hash_bucket::HashFunc<K>>        class unordered_map        {                struct map_KeyOfT                {                        const K& operator()(const pair<K, V>& kv) {                                return kv.first;                        }                };        public:                // 重定名哈希表的迭代器                typedef typename hash_bucket::HashTable<K, pair<const K, V>, map_KeyOfT, Hash>::Iterator iterator;                typedef typename hash_bucket::HashTable<K, pair<const K, V>, map_KeyOfT, Hash>::Const_Iterator const_iterator;                // 迭代器                iterator begin() {                        return _ht.begin();                }                iterator end() {                        return _ht.end();                }                const_iterator begin() const {                        return _ht.begin();                }                const_iterator end() const {                        return _ht.end();                }                // 其他利用:插入删除+查找                pair<iterator, bool> insert(const pair<K, V>& kv) {                        return _ht.insert(kv);                }                bool erase() {                        return _ht.erase();                }                iterator find(const K& key) {                        return _ht.find();                }                // operator[]                V& operator[](const K& key) {                        pair<iterator, bool> ret = _ht.insert({ key, V() });                        return ret.first->second;                }        private:                // 注意要加上类域指定,否则编译器报希奇的错误                hash_bucket::HashTable<K, pair<const K, V>, map_KeyOfT, Hash> _ht;        };}
复制代码

4.4 unordered_set 的完备代码

  1. #pragma once#include"HashTable.h"namespace my{        template<class K, class Hash = hash_bucket::HashFunc<K>>        class unordered_set        {                struct set_KeyOfT {                        const K& operator()(const K& key) {                                return key;                        }                };        public:                typedef typename hash_bucket::HashTable<K, const K, set_KeyOfT, Hash>::Iterator iterator;                typedef typename hash_bucket::HashTable<K, const K, set_KeyOfT, Hash>::Const_Iterator const_iterator;                // 迭代器                iterator begin() {                        return _ht.begin();                }                iterator end() {                        return _ht.end();                }                const_iterator begin() const {                        return _ht.begin();                }                const_iterator end() const {                        return _ht.end();                }                // 其他利用:插入删除+查找                pair<iterator, bool> insert(const K& key) {                        return _ht.insert(key);                }                bool erase() {                        return _ht.erase();                }                iterator find(const K& key) {                        return _ht.find();                }        private:                hash_bucket::HashTable<K, const K, set_KeyOfT, Hash> _ht;        };}
复制代码


5. 哈希表结构 完备代码:节点+迭代器+哈希函数+哈希表

  1. namespace hash_bucket
  2. {
  3.         template<class T>
  4.         struct HashNode {
  5.                 typedef HashNode<T> Node;
  6.                 T _data;
  7.                 Node* _next;
  8.                 HashNode(const T& data)
  9.                         :_data(data)
  10.                         ,_next(nullptr)
  11.                 {}
  12.         };
  13.         template<class K>
  14.         class HashFunc
  15.         {               
  16.         public:
  17.                 size_t operator()(const K& key) {
  18.                         return (size_t)key;
  19.                 }
  20.         };
  21.         template<>
  22.         class HashFunc<string>
  23.         {
  24.         public:
  25.                 size_t operator()(const string& s) {
  26.                         size_t n = 0;
  27.                         for (auto& ch : s) {
  28.                                 n += ch;  // 将字符串的每个字符的ASCLII码值相加起来,但这样还是不可完全避免冲突,如 abc 和 cba 的 ASCII码值总和是相等的,则 n 相等,取模之后也就冲突
  29.                                 // 缓解冲突的一个方法:每一个字符都相乘一个 31
  30.                                 n *= 31;
  31.                         }
  32.                         return n;
  33.                 }
  34.         };
  35.         // 前置声明,迭代器类中使用哈希表,会向上查找是否有哈希表,否则不匹配,需要提前声明
  36.         template<class K, class T, class KeyOfT, class Hash>
  37.         class HashTable;
  38.         // 迭代器
  39.         template <class K, class T, class Ref, class Ptr, class KeyOfT, class Hash = HashFunc<K>>
  40.         class HashIterator
  41.         {
  42.                 typedef HashNode<T> Node;
  43.                 typedef HashIterator<K, T, Ref, Ptr, KeyOfT, Hash> Self;
  44.         public:
  45.                 Node* _pNode;
  46.                 HashTable<K, T, KeyOfT, Hash>* HT; // 获取哈希表指针
  47.                 Ref operator*() {
  48.                         return _pNode->_data;
  49.                 }
  50.                 Ptr operator->() {
  51.                         return &(_pNode->_data);
  52.                 }
  53.                 bool operator!=(const Self& it) {
  54.                         return _pNode != it._pNode;
  55.                 }
  56.                 bool operator==(const Self& it) {
  57.                         return _pNode == it._pNode;
  58.                 }
  59.                 Self& operator++() {
  60.                         assert(_pNode);
  61.                         // 逻辑是 ++ 到下一个有效元素位置:因此需要判断下一个位置是否是空节点
  62.                         // 因为一个链表遍历完,需要重新到另一个哈希桶,因此还需要重新更新迭代器指向,需要获取哈希表信息,则传一个哈希表指针过来
  63.                         if (_pNode->_next != nullptr) {
  64.                                 _pNode = _pNode->_next;
  65.                         }
  66.                         else {
  67.                                 KeyOfT kot;
  68.                                 Hash hash;
  69.                                 size_t hashi = hash(kot(_pNode->_data)) % (HT->_table.size());
  70.                                 hashi++;  // 这样就跳到下一个桶了
  71.                                
  72.                                 // 如果下一个桶是空的,就继续跳
  73.                                 while (hashi < (HT->_table.size())) {
  74.                                         if (HT->_table[hashi]) break;
  75.                                         hashi++;  // 这样就跳到下一个桶了
  76.                                 }
  77.                                 if (hashi >= (HT->_table.size())) {
  78.                                         _pNode = nullptr;
  79.                                 }
  80.                                 else {
  81.                                         _pNode = HT->_table[hashi];
  82.                                 }
  83.                         }
  84.                         return *this;
  85.                 }
  86.                 HashIterator(Node* pNode, HashTable<K, T, KeyOfT, Hash>* pHT)
  87.                         :_pNode(pNode)
  88.                         , HT(pHT)
  89.                 {}
  90.         };
  91.         template<class K, class T, class KeyOfT, class Hash>
  92.         class HashTable
  93.         {
  94.                 // 将迭代器类设置成 友元,以便迭代器类可以访问该类的私有
  95.                 template <class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>
  96.                 friend class HashIterator;
  97.                 typedef HashNode<T> Node;
  98.         public:
  99.                 typedef HashIterator<K, T, T&, T*, KeyOfT, Hash> Iterator;
  100.                 typedef HashIterator<K, T, const T&, const T*, KeyOfT, Hash> Const_Iterator;
  101.        
  102.                 HashTable() {
  103.                         // 一开始vector里面是随机值,不是 nullptr!! 要自己初始化
  104.                         _table.resize(10, nullptr);
  105.                 }
  106.                 // 本哈希桶需要显式写析构:因为 Node* 是内置类型,只会默认析构成 nullptr,链表节点不会被处理
  107.                 ~HashTable() {
  108.                         Node* cur = nullptr;
  109.                         Node* next = nullptr;
  110.                         for (size_t i = 0; i < _table.size(); ++i) {
  111.                                 cur = _table[i];
  112.                                 while (cur) {
  113.                                         next = cur->_next;
  114.                                         delete cur;
  115.                                         cur = next;
  116.                                 }
  117.                         }
  118.                 }
  119.                 // 迭代器
  120.                 Iterator begin() {
  121.                         // 先找到
  122.                         for (size_t i = 0; i < _table.size(); ++i) {
  123.                                 if (_table[i]) return Iterator(_table[i], this);
  124.                         }
  125.                 }
  126.                 Iterator end() {
  127.                         return Iterator(nullptr, this);
  128.                 }
  129.                 Const_Iterator begin() const {
  130.                         // 先找到
  131.                         for (size_t i = 0; i < _table.size(); ++i) {
  132.                                 if (_table[i]) return Const_Iterator(_table[i], this);
  133.                         }
  134.                 }
  135.                 Const_Iterator end() const {
  136.                         return Const_Iterator(nullptr, this);
  137.                 }
  138.                 Iterator find(const K& key) {
  139.                         size_t hashi = hash(key) % _table.size();
  140.                          
  141.                         Node* cur = _table[hashi];
  142.                         while (cur) {
  143.                                 if (kot(cur->_data) == key) return Iterator(cur, this);
  144.                                 cur = cur->_next;
  145.                         }
  146.                         return end();
  147.                 }
  148.                 pair<Iterator, bool> insert(const T& data) {
  149.                         // 考虑冗余
  150.                         Iterator ret = find(kot(data));
  151.                         if (ret != end()) {
  152.                                 cout << "该数据已存在" << '\n';
  153.                                 return make_pair(ret, false);
  154.                         }
  155.                         // 考虑扩容:当负载比率为 1 时,扩容(即 n == size)
  156.                         // 甚至可以大于 1,即理想情况下,就是平均每个桶有 1个以上
  157.                         if (_n == _table.size()) {
  158.                                 HashTable<K, T, KeyOfT, Hash> newTable;
  159.                                 newTable._table.resize(2 * _table.size());
  160.                                 // 若直接遍历每个节点,取数值 data insert 插入新表,会导致频繁的 new 节点,造成一定消耗
  161.                                 // 我们可以直接将旧表的节点 直接转接到 新表,省去new节点的消耗
  162.                                 for (size_t i = 0; i < _table.size(); ++i) {
  163.                                         Node* cur = _table[i];
  164.                                         /*while (cur) {
  165.                                                 newTable.insert(cur->_data);
  166.                                                 cur = cur->_next;
  167.                                         }*/
  168.                                         while (cur) {
  169.                                                 Node* Next = cur->_next;
  170.                                                 size_t hashi = hash(kot(cur->_data)) % newTable._table.size();
  171.                                                 cur->_next = newTable._table[hashi];
  172.                                                 newTable._table[hashi] = cur;
  173.                                                 cur = Next;
  174.                                         }
  175.                                         _table[i] = nullptr;
  176.                                 }
  177.                                 _table.swap(newTable._table);
  178.                         }
  179.                         size_t hashi = hash(kot(data)) % _table.size();
  180.                         // 头插和尾插都行:头插最方便
  181.                         Node* newNode = new Node(data);
  182.                         newNode->_next = _table[hashi];
  183.                         _table[hashi] = newNode;
  184.                         _n++;
  185.                         return make_pair(Iterator(newNode, this), true);
  186.                 }
  187.                 bool erase(const K& key) {
  188.                         Iterator ret = find(kot(data));
  189.                         if (ret._pNode == nullptr) {
  190.                                 cout << "该节点不存在" << '\n';
  191.                                 return false;
  192.                         }
  193.                         // 删除函数需要自己找目标节点,因为底层是单链表,没有prev,删除不好搞
  194.                         size_t hashi = hash(key) % _table.size();
  195.                         Node* cur = _table[hashi];
  196.                         Node* prev = nullptr;
  197.                         while (cur) {
  198.                                 if (kot(cur->_data) == key) {
  199.                                         if (prev == nullptr) _table[hashi] = cur->_next;
  200.                                         else prev->_next = cur->_next;
  201.                                         delete cur;
  202.                                         cur = nullptr;
  203.                                         --_n;
  204.                                         return true;
  205.                                 }
  206.                                 prev = cur;
  207.                                 cur = cur->_next;
  208.                         }
  209.                         return false;
  210.                 }
  211.         private:
  212.                 //也可以使用 list:双向带头循环链表
  213.                 //vector<list<pair<K, V>>> _table;  
  214.                 vector<Node*> _table;
  215.                 size_t _n = 0; // 负载因子
  216.                 Hash hash;
  217.                 KeyOfT kot;
  218.         };
  219. }
复制代码

 unordered_map / unordered_set 的 测试代码

  1. void test_map() {
  2.         my::unordered_map<string, string> dict;
  3.         dict.insert({ "sort", "排序" });
  4.         dict.insert({ "left", "左边" });
  5.         dict.insert({ "right", "右边" });
  6.         //dict["left"] = "左边,剩余";
  7.         dict["insert"] = "插入";
  8.         dict["string"];
  9.         my::unordered_map<string, string>::iterator it = dict.begin();
  10.         while (it != dict.end())
  11.         {
  12.                 // 不能修改first,可以修改second
  13.                 //it->first += 'x';
  14.                 it->second += 'x';
  15.                 cout << it->first << ":" << it->second << endl;
  16.                 ++it;
  17.         }
  18.         cout << endl;
  19. }
  20. void test_set() {
  21.         my::unordered_set<string> st;
  22.         st.insert({ "sort" });
  23.         st.insert({ "left" });
  24.         st.insert({ "right" });
  25.         /*dict["left"] = "左边,剩余";
  26.         dict["insert"] = "插入";
  27.         dict["string"];*/
  28.         my::unordered_set<string>::iterator it = st.begin();
  29.         while (it != st.end())
  30.         {
  31.                 // 不能修改*it
  32.                 //*it += 'x';
  33.                 cout << *it <<  endl;
  34.                 ++it;
  35.         }
  36.         cout << endl;
  37. }
复制代码




免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!qidao123.com:ToB企服之家,中国第一个企服评测及软件市场,开放入驻,技术点评得现金

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表