unordered_map 和 unordered_set

unordered —— 无序的,从表面上来看,与 map 和 set 不同之处就在于,unordered_map 和 unordered_set 无法保证插入数据是有序的;

尽管如此,由于这两种容器内部封装了“哈希桶”,可以实现快速查找数据 —— 这一优点与 map 和 set 相同。

其实,除了内部结构的不同外,其余与 map 和 set 没什么不同,一样的 insert、find、erase … … 在我们模拟过 map set 的基础上,再学习封装 无序map 和 无序set 实在简单。

因此,本文的重点在于迭代器的运行逻辑 —— operator++() ,和理解模板、仿函数等

最后,补充一个概念:

哈希 是一种思想,将传入的数据映射成一个或多个整型;哈希表 / 哈希桶 则是一种实现哈希思想的结构。

一、改造哈希桶

1.1 初步搭建 HashTable
	// 改造 HashNodetemplate<class T> struct HashNode{HashNode<T>* _next; T _data;HashNode(const T& data):_next(nullptr),_data(data){}};// 改造 HashTable// 此处 HashFunc 与《开散列哈希桶》中提供的无异// KeyOfT 与 map set 中的无异,都是用于从 T 中取到键值 template<class K, class T, class KeyOfT, class Hash = HashFunc<K>> class HashTable{public:typedef HashNode<T> Node;private:vector<Node*> _tables;size_t _n = 0;};
1.2 数据插入 数据查找
  • 数据插入 —— Insert()
	pair<iterator, bool> Insert(const T& data){KeyOfT kot;iterator ret = Find(kot(data)); // 未实现的 Find,返回值为 iteratorif (ret != end())// 找到了{return make_pair(ret, false);}// 扩容 // ... // 插入Hash hs;size_t hashi = hs(kot(data)) % _tables.size(); // kot(data) -- 取键值;hs(键值) -- 计算映射值Node* newNode = new Node(data);newNode->_next = _tables[hashi];_tables[hashi] = newNode;_n++;return make_pair(new_iterator, true); // 此处为伪代码!}

与普通哈希桶不同的地方在于此: size_t hashi = hs(kot(data)) % _tables.size(); // kot(data) -- 取键值;hs(键值) -- 计算映射值 ,多套了一层仿函数。

PS:

  1. 扩容逻辑与哈希桶完全一致。

  2. return make_pair(new_iterator, true); 为伪代码,用 new_iterator 代表插入节点的迭代器 —— 后面介绍迭代器时,会将这里的坑填上!

  • 数据查找 —— Find()

很多新手不理解为什么在封装 map set 要这样构造 —— 好像传入了两个 Key :

map: RBTree<K, pair<const K, V>, ... > _t;

set: RBTree<K, const K, ...> _t;

我们通常是 t.find(key1); t,erase(key2); 这种方式使用 find() 和 erase() ,无论 t 是 map 还是 set

意思就是,第一个模板参数 K 是为了解决 map 的查找和删除等的问题

	iterator Find(const K& key){Hash hs;KeyOfT kot;size_t hashi = hs(key) % _tables.size();Node* cur = _tables[hashi];while (cur){if (kot(cur->_data) == key){return iterator_cur; // 这里同样是伪代码!}cur = cur->_next;}return iterator_nullptr;// 伪代码}

iterator_cur 代表 cur 位置的迭代器; iterator_nullptr 代表 空迭代器,这两个迭代器的空白会在后面填补。

二、迭代器封装 __Hash_Iterator

__Hash_Iterator 内部应该传入什么呢?节点的指针吗?哈希桶的指针吗?

我们希望可以通过迭代器遍历整个哈希桶,同时要能取到当前迭代器所在节点的数据,因此,迭代器内部应有节点的指针和哈希桶的指针。

	template<class K, class T, class KeyOfT, class Hash = HashFunc<K>> struct __Hash_Iterator{typedef HashNode<T> Node;typedef HashTable<K, T, KeyOfT, Hash> HashTable;typedef __Hash_Iterator<K, T, KeyOfT, Hash> Self;Node* _node;HashTable* _ht;__Hash_Iterator(Node* node, HashTable* ht):_node(node),_ht(ht){}};
2.1 operator++()

operator++() 需要考虑两种情形:

  1. _node->_next 不为空,++ 后,_node = _node->_next
  2. _node->_next 为空,则往后遍历 HashTable,直到找到下一个不为空的位置,或者遍历完整个 HashTable 。
	Self& operator++(){if (_node->_next){_node = _node->_next;}else {Hash hs;KeyOfT kot;size_t hashi = hs(kot(_node->_data)) % _ht->_tables.size();hashi++; // 从当前位置的后一个位置开始查找while (hashi < _ht->_tables.size()){if (_ht->_tables[hashi]){// 找到下一个位置,跳出循环_node = _ht->_tables[hashi]; break;}++hashi;}if (hashi == _ht->_tables.size()) // 遍历结束,没有下一个节点{_node = nullptr;}}return *this;}
2.2 operator!=() operator*() operator->()
	bool operator!=(const Self& s){return _node != s._node;}T& operator*(){return _node->_data;}T* operator->(){return &_node->_data;}
2.3 完善 HashTable

针对第一部分中迭代器遗留问题,在这里将其完善。

	template<class K, class T, class KeyOfT, class Hash = HashFunc<K>> class HashTable{public:typedef HashNode<T> Node;typedef __Hash_Iterator<K, T, KeyOfT, Hash> iterator;pair<iterator, bool> Insert(const T& data){KeyOfT kot;Hash hs;iterator ret = Find(kot(data)); // 未实现的 Find,返回值为 iteratorif (ret != end())// 找到了{return make_pair(ret, false);}// 扩容 if (_n == _tables.size()){vector<Node*> newTables(_tables.size() * 2, nullptr);for (size_t i = 0; i < _tables.size(); ++i){Node* cur = _tables[i];while (cur){Node* next = cur->_next;size_t hashi = hs(kot(cur->_data)) % newTables.size();cur->_next = newTables[hashi];newTables[hashi] = cur;cur = next;}_tables[i] = nullptr;}_tables.swap(newTables);}// 插入size_t hashi = hs(kot(data)) % _tables.size(); // kot(data) -- 取键值;hs(键值) -- 计算映射值Node* newNode = new Node(data);newNode->_next = _tables[hashi];_tables[hashi] = newNode;_n++;return make_pair(iterator(newNode, this), true); // }iterator Find(const K& key){Hash hs;KeyOfT kot;size_t hashi = hs(key) % _tables.size();Node* cur = _tables[hashi];while (cur){if (kot(cur->_data) == key){return iterator(cur, this); }cur = cur->_next;}return iterator(nullptr, this);}bool Erase(const K& key){Hash hs;KeyOfT kot;size_t hashi = hs(key) % _tables.size();Node* cur = _tables[hashi];Node* prev = nullptr;while (cur){if (kot(cur->_data) == key){if (prev == nullptr) // cur == _tables[hashi]{_tables[hashi]->_next = cur->_next;}else{prev->_next = cur->_next;}delete cur;--_n;return true;}prev = cur;cur = cur->_next;}return false;}private:vector<Node*> _tables;size_t _n = 0;};
2.4 begin() end()
	iterator begin(){for (size_t i = 0; i < _tables.size(); i++){if (_tables[i]){return iterator(_tables[i], this);}}return iterator(nullptr, this);}iterator end(){return iterator(nullptr, this);}

三、unordered_map unordered_set 封装

unordered_map

	template<class K, class V, class Hash = HashFunc<K>>class unordered_map{public:struct MapKeyOfT{K operator()(const pair<K, V>& kv){return kv.first;}};typedef typename HashTable<K, pair<const K, V>, MapKeyOfT, Hash>::iterator iterator;pair<iterator, bool> insert(const pair<K, V>& kv){return _ht.Insert(kv);}iterator find(const K& key){return _ht.Find(key);}iterator begin(){return _ht.begin();}iterator end(){return _ht.end();}V& operator[](const K& key){pair<iterator, bool> ret = insert(make_pair(key, V()));return ret.first->second;}private:HashTable<K, pair<const K, V>, MapKeyOfT, Hash> _ht;};

unordered_set

	template<class K, class Hash = HashFunc<K>>class unordered_set{public:struct SetKeyOfT{K operator()(const K& key){return key;}};typedef typename HashTable<K, const K, SetKeyOfT, Hash>::iterator iterator;iterator begin(){return _ht.begin();}iterator end(){return _ht.end();}pair<iterator, bool> insert(const K& key){return _ht.Insert(key);}iterator find(const K& key){return _ht.Find(key);}private:HashTable<K, const K, SetKeyOfT, Hash> _ht;};
注意:

如果直接将以上代码在 VS 中运行,会出现以下几个错误:

该问题的原因是,__Hash_Iterator 之前并未声明 HashTable

	template<class K, class T, class KeyOfT, class Hash> // 不能加缺省值class HashTable; // 声明template<class K, class T, class KeyOfT, class Hash = HashFunc<K>>struct __Hash_Iterator{// ...};

该问题在于,__Hash_Iterator 无法访问 HashTableprivate 成员变量,解决办法是将 __Hash_Iterator 写成 HashTable 的友元类

	template<class K, class T, class KeyOfT, class Hash = HashFunc<K>>class HashTable{public:template<class K, class T, class KeyOfT, class Hash> // 不能加缺省值friend struct __Hash_Iterator; // 友元// ...};

四、完整代码

My_Unordered_Map.h
#include "HashTable.h"namespace MY_Test
{template<class K, class V, class Hash = HashFunc<K>>class unordered_map{public:struct MapKeyOfT{K operator()(const pair<K, V>& kv){return kv.first;}};typedef typename HashTable<K, pair<const K, V>, MapKeyOfT, Hash>::iterator iterator;pair<iterator, bool> insert(const pair<K, V>& kv){return _ht.Insert(kv);}iterator find(const K& key){return _ht.Find(key);}iterator begin(){return _ht.begin();}iterator end(){return _ht.end();}V& operator[](const K& key){pair<iterator, bool> ret = insert(make_pair(key, V()));return ret.first->second;}private:HashTable<K, pair<const K, V>, MapKeyOfT, Hash> _ht;};void test_map1(){unordered_map<string, string> dict;dict.insert(make_pair("sort", "排序"));dict.insert(make_pair("left", "左边"));dict.insert(make_pair("right", "右边"));for (auto& kv : dict){//kv.first += 'x';kv.second += 'y';cout << kv.first << ":" << kv.second << endl;}}void test_map2(){string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜","苹果", "香蕉", "苹果", "西瓜", "香蕉", "草莓" };unordered_map<string, int> countMap;for (auto& e : arr){/*if (e == "ݮ"){int i = 0;}*/countMap[e]++;}for (auto& kv : countMap){cout << kv.first << ":" << kv.second << endl;}cout << endl;}
}
My_Unordered_Set.h
#include "HashTable.h"namespace MY_Test
{template<class K, class Hash = HashFunc<K>>class unordered_set{public:struct SetKeyOfT{K operator()(const K& key){return key;}};typedef typename HashTable<K, const K, SetKeyOfT, Hash>::iterator iterator;iterator begin(){return _ht.begin();}iterator end(){return _ht.end();}pair<iterator, bool> insert(const K& key){return _ht.Insert(key);}iterator find(const K& key){return _ht.Find(key);}private:HashTable<K, const K, SetKeyOfT, Hash> _ht;};void test_set1(){unordered_set<int> us;us.insert(3);us.insert(1);us.insert(5);us.insert(15);us.insert(45);us.insert(7);unordered_set<int>::iterator it = us.begin();while (it != us.end()){//*it += 100;cout << *it << " ";++it;}cout << endl;for (auto e : us){cout << e << " ";}cout << endl;}
}
HashTable.h
#include <vector>template<class K>
struct HashFunc
{size_t operator()(const K& key){size_t hash = key;return hash;}
};template<>
struct HashFunc<string>
{size_t operator()(const string& s){size_t hash = 0;for (auto e : s){hash = hash * 131 + e;}return hash;}
};template<class T>
struct HashNode
{HashNode<T>* _next;T _data;HashNode(const T& data):_next(nullptr), _data(data){}
};template<class K, class T, class KeyOfT, class Hash> // 不能加缺省值
class HashTable; // 声明template<class K, class T, class KeyOfT, class Hash = HashFunc<K>>
struct __Hash_Iterator
{typedef HashNode<T> Node;typedef HashTable<K, T, KeyOfT, Hash> HashTable;typedef __Hash_Iterator<K, T, KeyOfT, Hash> Self;Node* _node;HashTable* _ht;__Hash_Iterator(Node* node, HashTable* ht):_node(node), _ht(ht){}Self& operator++(){if (_node->_next){_node = _node->_next;}else{Hash hs;KeyOfT kot;size_t hashi = hs(kot(_node->_data)) % _ht->_tables.size();hashi++; // 从当前位置的后一个位置开始查找while (hashi < _ht->_tables.size()){if (_ht->_tables[hashi]){// 找到下一个位置,跳出循环_node = _ht->_tables[hashi];break;}++hashi;}if (hashi == _ht->_tables.size()) // 遍历结束,没有下一个节点{_node = nullptr;}}return *this;}bool operator!=(const Self& s){return _node != s._node;}T& operator*(){return _node->_data;}T* operator->(){return &_node->_data;}
};template<class K, class T, class KeyOfT, class Hash = HashFunc<K>>
class HashTable
{
public:typedef HashNode<T> Node;typedef __Hash_Iterator<K, T, KeyOfT, Hash> iterator;template<class K, class T, class KeyOfT, class Hash> // 不能加缺省值friend struct __Hash_Iterator; // 友元HashTable(){_tables.resize(10);}pair<iterator, bool> Insert(const T& data){KeyOfT kot;Hash hs;iterator ret = Find(kot(data)); // 未实现的 Find,返回值为 iteratorif (ret != end())// 找到了{return make_pair(ret, false);}// 扩容 if (_n == _tables.size()){vector<Node*> newTables(_tables.size() * 2, nullptr);for (size_t i = 0; i < _tables.size(); ++i){Node* cur = _tables[i];while (cur){Node* next = cur->_next;size_t hashi = hs(kot(cur->_data)) % newTables.size();cur->_next = newTables[hashi];newTables[hashi] = cur;cur = next;}_tables[i] = nullptr;}_tables.swap(newTables);}// 插入size_t hashi = hs(kot(data)) % _tables.size(); // kot(data) -- 取键值;hs(键值) -- 计算映射值Node* newNode = new Node(data);newNode->_next = _tables[hashi];_tables[hashi] = newNode;_n++;return make_pair(iterator(newNode, this), true); // }iterator Find(const K& key){Hash hs;KeyOfT kot;size_t hashi = hs(key) % _tables.size();Node* cur = _tables[hashi];while (cur){if (kot(cur->_data) == key){return iterator(cur, this);}cur = cur->_next;}return iterator(nullptr, this);}bool Erase(const K& key){Hash hs;KeyOfT kot;size_t hashi = hs(key) % _tables.size();Node* cur = _tables[hashi];Node* prev = nullptr;while (cur){if (kot(cur->_data) == key){if (prev == nullptr) // cur == _tables[hashi]{_tables[hashi]->_next = cur->_next;}else{prev->_next = cur->_next;}delete cur;--_n;return true;}prev = cur;cur = cur->_next;}return false;}iterator begin(){for (size_t i = 0; i < _tables.size(); i++){if (_tables[i]){return iterator(_tables[i], this);}}return iterator(nullptr, this);}iterator end(){return iterator(nullptr, this);}private:vector<Node*> _tables;size_t _n = 0;
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/698349.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C++】深度解析:用 C++ 模拟实现 String 类,探索其底层实现细节

目录 了解string类 string的内存管理 VS下string的结构 ​g下string的结构 string的模拟实现 string的构造函数 浅拷贝 深拷贝 string的遍历 重载 [] 下标访问 迭代器访问 reserve resize 增删查改 push_back() append和 insert和erase find substr swap 流插入…

自然资源-“十四五”规划引领,审批智慧化提升-值得学习

自然资源-“十四五”规划引领&#xff0c;审批智慧化提升-值得学习 2022年1月12日&#xff0c;国务院正式印发了《“十四五”数字经济发展规划》&#xff08;国发〔2021〕29号&#xff09;&#xff0c;从八个方面对“十四五”期间我国数字经济发展做出总体部署。其中第五点要求…

SpringBoot报空指针错:java.lang.NullPointerException

虽然报空指针错误的原因可能有很多种&#xff0c;但是我还是写上我的报错原因&#xff0c;以此与各位共勉~ 在这里提前说一句&#xff0c;AI虽然强大&#xff0c;但是还是要谨慎使用啊(血的教训)~ 这里先截图我错误的地方&#xff1a; 前端能成功传进来值&#xff0c;后台控制…

【运维自动化-配置平台】如何自动应用主机属性

主要用于配置主机属性的自动应用。当主机发生模块转移或模块新加入主机时&#xff0c;会根据目标模块配置的策略自动触发修改主机属性&#xff0c;比如主机负责人、主机状态。主机属性自动应用顾名思义是应用到主机上&#xff0c;而主机是必须在模块下的&#xff0c;所以有两种…

花趣短视频源码淘宝客系统全开源版带直播带货带自营商城流量主小游戏功能介绍

1、首页仿抖音短视频 &#xff0c;关注 &#xff0c;我的 本地 直播 可发布短视频 可录制上传 2、商城页面 广告位、淘口令识别、微信登录、淘宝登录、淘宝返佣、拼多多返佣、京东返佣、唯品会返佣、热销榜、聚划算、天猫超市、9.9包邮、品牌特卖、新人攻略 、小米有品、优惠加…

盘他系列——oj!!!

1.Openjudge 网站: OpenJudge 2.洛谷 网站: 首页 - 洛谷 | 计算机科学教育新生态 3.环球OJ 网站: QOJ - QOJ.ac 4. 北京大学 OJ:Welcome To PKU JudgeOnline 5.自由OJ 网站: https://loj.ac/ 6.炼码 网站:LintCode 炼码 8.力扣 网站: 力扣 9.晴练网首页 - 晴练网

在线视频教育平台,基于 SpringBoot+Vue+MySQL 开发的前后端分离的在线视频教育平台设计实现

目录 一. 前言 二. 功能模块 2.1. 用户功能模块 2.2. 管理员功能模块 2.3. 教师功能模块 2.4. 前台首页功能模块 三. 部分代码实现 四. 源码下载 一. 前言 随着科学技术的飞速发展&#xff0c;各行各业都在努力与现代先进技术接轨&#xff0c;通过科技手段提高自身的优…

部分树上问题及图的联通性(图论学习总结部分内容)

文章目录 前言三、部分树上问题及图的联通性最小生成树知识点例题 e g 1 : eg1: eg1: 走廊泼水节&#xff08;克鲁斯卡尔思想的灵活运用&#xff09; e g 2 &#xff1a; eg2&#xff1a; eg2&#xff1a; B-Picnic Planning e g 3 eg3 eg3&#xff1a;L - Classic Problem&…

探索ISP静态:网络连接的稳定基石

在数字化时代的浪潮中&#xff0c;互联网已成为我们生活、工作、学习不可或缺的一部分。而网络连接的质量&#xff0c;直接决定了我们在线体验的好坏。在众多网络连接技术中&#xff0c;“ISP静态”作为一种稳定、可靠的网络连接方式&#xff0c;越来越受到广大用户的青睐。本文…

微服务熔断降级

什么是熔断降级 微服务中难免存在服务之间的远程调用&#xff0c;比如&#xff1a;内容管理服务远程调用媒资服务的上传文件接口&#xff0c;当微服务运行不正常会导致无法正常调用微服务&#xff0c;此时会出现异常&#xff0c;如果这种异常不去处理可能导致雪崩效应。 微服…

redis原生命令及项目使用

主动更新策略 缓存问题及解决 布隆过滤出现哈希冲突解决方案: 选择合适的哈希函数:布隆过滤器的性能和哈希函数的选择密切相关。选择高效、低碰撞率的哈希函数可以降低误判率。通常使用的哈希函数有 MurmurHash、FNV 等。 合理设置过滤器大小:过滤器的大小(位数组的大小)…

资料同化 | 搭建docker环境-1

Community Gridpoint Statistical Interpolation (GSI) system DTC 是一个分布式设施&#xff0c;NWP 社区可以在这里测试和评估用于研究和操作的新模型和技术。 DTC的目标包括&#xff1a; 链接研究和操作社区 研究成果转化为实际操作的速度 加快改善天气预报 开发和测试有…