C++进阶--使用哈希表实现unordered_map和unordered_set的原理与实例

本文将介绍如何使用哈希表来实现C++ STL库中的unordered_map和unordered_set容器。我们将会解释哈希表的基本原理,并给出具体的代码示例,帮助读者更好地理解和应用哈希表。

哈希原理讲解–链接入口

set和map的实现的文章,与unordered_map实现类似

对HashTable的改造

#pragma oncetemplate<class K>
struct Hashfunc
{size_t operator()(const K& key){return (size_t)key;}
};
//特化
template<>
struct Hashfunc<string>
{size_t operator()(const string& s){size_t hash = 0;for (auto a : s){hash += a;hash *= 31;}return hash;}
};namespace hash_bucket
{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>struct _HTIterator{typedef HashNode<T> Node;typedef HashTable<K, T, KeyOfT, Hash> HT;typedef _HTIterator<K, T, KeyOfT, Hash> Self;Node* _node;HT* _ht;_HTIterator(Node* node,HT* ht):_node(node),_ht(ht){}T& operator*(){return _node->_data;}T* operator->(){return &_node->_data;}Self& operator++(){if (_node->_next){_node = _node->_next;}else{KeyOfT kot;Hash hs;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;}bool operator ==(const Self& s){return _node == s._node;}};template<class K,class T,class KeyOfT, class Hash>class HashTable{template<class K,class T,class KeyOfT,class Hash>friend struct _HTIterator;typedef HashNode<T> Node;public:typedef _HTIterator<K, T, KeyOfT, Hash> iterator;iterator begin(){for (size_t i = 0; i < _tables.size(); i++){// 找到第一个桶的第一个节点if (_tables[i]){return iterator(_tables[i], this);}}return end();}iterator end(){return iterator(nullptr, this);}HashTable(){_tables.resize(10, nullptr);_n = 0;}~HashTable(){for (size_t i= 0; i < _tables.size(); i++){Node* cur = _tables[i];while (cur){Node* next = cur->_next;delete cur;cur = next;}_tables[i] = nullptr;}}iterator Find(const K& key){KeyOfT kot;Hash hs;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);}pair<iterator,bool> Insert(const T& data){KeyOfT kot;Hash hs;//查找是否有相同元素iterator it = Find(kot(data));if (it != end())return make_pair(it,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();Node* newnode = new Node(data);it = iterator(newnode, this);newnode->_next = _tables[hashi];_tables[hashi] = newnode;++_n;return make_pair(it,true);}bool Erase(const K& key){KeyOfT kot;Hash hs;size_t hashi = hs(key) % _tables.size();Node* prev = nullptr;Node* cur = _tables[hashi];while (cur){if (kot(cur->_data)==key){if (prev){prev->_next = cur->_next;}else{_tables[hashi] = cur->_next;}delete cur;_n--;return true;}prev = cur;cur = cur->_next;}return false;}private:vector<Node*> _tables;size_t _n;};
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

对unordered_map和unordered_set的实现

unordered_set是一种集合存储的容器,它存储唯一的元素。我们同样可以使用哈希表来实现unordered_set,将元素映射到数组的索引。

  • 插入操作:通过哈希函数计算元素的索引,如果索引位置为空,则直接插入;否则,使用开放地址法寻找下一个可用的槽进行插入。
  • 查找操作:通过哈希函数计算元素的索引,然后比较该索引位置的元素是否与目标元素相等,如相等则返回存在;如不相等,则使用开放地址法依次查找下一个槽,直到找到相等的元素或者遇到空槽为止。
  • 删除操作:通过哈希函数计算元素的索引,然后通过循环找到对应的元素,如果这个元素有前一个结点,那么就让前一个结点链上当前结点的下一个结点,然后删除当前结点;如果当前元素没有前一个结点,那么就表示删除该节点后,哈希桶的头结点将为空;
namespace fnc
{template<class K,class Hash=Hashfunc<K>>class unordered_set{struct SetKeyOfT{const K& operator()(const K& key){return key;}};public:typedef typename hash_bucket::HashTable<K, const K, SetKeyOfT, Hash>::iterator iterator;iterator begin(){return _ht.begin();}iterator end(){return _ht.end();}pair<iterator,bool> insert(const K& k){return _ht.Insert(k);}iterator find(const K& k){return _ht.Find(k);}bool Erase(const K& k){return _ht.Erase(k);}private:hash_bucket::HashTable<K, const K, SetKeyOfT, Hash> _ht;};
}

unordered_map是一种键值对存储的容器,它允许通过键快速查找对应的值。我们可以使用哈希表来实现unordered_map,将键映射到数组的索引,值存储在对应的槽中。

  • 插入操作:通过哈希函数计算键的索引,如果索引位置为空,则直接插入;否则,使用开放地址法寻找下一个可用的槽进行插入。
  • 查找操作:通过哈希函数计算键的索引,然后比较该索引位置的键是否与目标键相等,如相等则返回对应的值;如不相等,则使用开放地址法依次查找下一个槽,直到找到相等的键或者遇到空槽为止。
  • 删除操作:通过哈希函数计算元素的索引,然后通过循环找到对应的元素,如果这个元素有前一个结点,那么就让前一个结点链上当前结点的下一个结点,然后删除当前结点;如果当前元素没有前一个结点,那么就表示删除该节点后,哈希桶的头结点将为空;
namespace fnc
{template<class K, class V, class Hash = Hashfunc<K>>class unordered_map{struct MapKeyOfT{const K& operator()(const pair<K, V>& kv){return kv.first;}};public:typedef typename hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT, Hash>::iterator iterator;iterator begin(){return _ht.begin();}iterator end(){return _ht.end();}pair<iterator,bool> insert(const pair<K, V>& kv){return _ht.Insert(kv);}iterator find(const K& k){return _ht.Find(k);}bool Erase(const K& k){return _ht.Erase(k);}V& operator[](const K& key){pair<iterator, bool> ret = insert(make_pair(key, V()));return ret.first->second;}private:hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT, Hash> _ht;};
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

测试

insert

在这里插入图片描述
在这里插入图片描述

find和erase

//find和erasevoid test_set2(){vector<int> v = { 3,1,5,15,45,7 };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()){cout << *it << " ";++it;}cout << endl;if (us.find(15) != us.end()){cout << "15:在" << endl;}else{cout << "15:不在" << endl;}if (us.find(16) != us.end()){cout << "16:在" << endl;}else{cout<<"16:不在" << endl;}cout << endl;us.Erase(5);us.Erase(7);it = us.begin();while (it != us.end()){cout << *it << " ";++it;}cout << endl;}
}
void test_map2(){unordered_map<string, string> dict;dict.insert(make_pair("sort", "排序"));dict.insert(make_pair("left", "左"));dict.insert(make_pair("right", "右"));for (auto& kv : dict){cout << kv.first << ":" << kv.second << endl;}cout << endl;fnc::unordered_map<string, string>::iterator it = dict.find("sort");if (it!=dict.end()){cout <<"找到了:" << (*it).first << ":" << (*it).second << endl;}cout << endl;dict.Erase("left");for (auto& kv : dict){cout << kv.first << ":" << kv.second << endl;}}

在这里插入图片描述

operator[]

void test_map3(){vector<string> v = { "香蕉","苹果","橙子","西瓜","香蕉","苹果","橙子","西瓜","香蕉","苹果","橙子" ,"香蕉","苹果" };unordered_map<string, int> dict;for (auto a : v){dict[a]++;}for (auto a : dict){cout << a.first << ":" << a.second << endl;}}

在这里插入图片描述

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

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

相关文章

代码随想录 Day59 单调栈

42接雨水问题&#xff0c;很巧妙&#xff0c;我一开始的思路是需要两个单调栈&#xff0c;一个是递增&#xff0c;一个是递减&#xff0c;分别遍历&#xff0c;从而得到当前方块的与两边的高度差&#xff0c;但是看过卡哥的思路&#xff0c;就会明白其实另一次的比较已经在入栈…

利用AI技术预测未被充分监测的流域中的极端洪水事件笔记

利用人工智能&#xff08;AI&#xff09;技术预测未被充分监测的流域&#xff08;ungauged watersheds&#xff09;中的极端洪水事件 文章目录 利用人工智能&#xff08;AI&#xff09;技术预测未被充分监测的流域&#xff08;ungauged watersheds&#xff09;中的极端洪水事件…

初学者怎么学习Python?Python学习从什么开始?

学习Python&#xff0c;可以先从Python爬虫开始哈 首选&#xff0c;爬虫并不是网上传言的那样&#xff0c;动不动就面向铁窗编程等&#xff0c;正规的爬虫还是相当有市场的&#xff01;&#xff01;&#xff01; 而 Python 作为入门简易的语言&#xff0c;语法也相当简洁&…

Linux-2 Linux的权限

目录 1.什么是权限&#xff1f; 2.权限的本质 3.Linux中的用户 普通用户与root用户相互转换 普通用户不变root&#xff0c;以root身份执行一个命令 LInux中的角色 4.Linux文件的权限 5.快速掌握修改权限的做法 修改权限 6.对比权限有无表现 对于普通用户&#xf…

1+x中级题目练习复盘(20220625 1+X 中级理论考试)

Override 用于标注重写方法 函数式接口是指有且只有一个抽象方法的接口&#xff1b;

DP背包模型

目录 采药&#xff08;01背包&#xff09;代码实现 装箱问题&#xff08;01背包&#xff09;代码实现 *宠物小精灵之收服&#xff08;二维费用01背包&#xff09;题目分析代码实现 数字组合&#xff08;01背包&#xff09;代码实现 买书&#xff08;完全背包&#xff09;代码实…

Chrome 插件 tabs API 解析

Chrome.tabs API 解析 使用 chrome.tabs API 与浏览器的标签页系统进行交互&#xff0c;可以使用此 API 在浏览器中创建、修改和重新排列标签页 Tabs API 不仅提供操作和管理标签页的功能&#xff0c;还可以检测标签页的语言、截取屏幕截图&#xff0c;以及与标签页的内容脚本…

linux之zabbix自定义监控

zabbix基本配置见&#xff1a;写文章-CSDN创作中心https://mp.csdn.net/mp_blog/creation/editor/136783672 自定义监控规则 命令为who | wc -l 显示为2&#xff0c;主机一个&#xff0c;mobaxterm一个&#xff0c;思路是开启3个终端&#xff0c;让主机的zabbix服务自动检测1…

报名PMP考试需要满足哪些条件?

报名条件如下&#xff1a; 项目经验这一点&#xff0c;PMP报考人员是需要有项目管理经验&#xff0c;而所说的具有多少小时的项目管理经验&#xff0c;指的是项目相关经验&#xff0c;比如参与项目研发、测试、交付、运维、技术支持、售前等&#xff0c;这个项目经验也是一个国…

【数据分享】1929-2023年全球站点的逐日平均海平面压力(Shp\Excel\免费获取)

气象数据是在各项研究中都经常使用的数据&#xff0c;气象指标包括气温、风速、降水、能见度等指标&#xff0c;说到气象数据&#xff0c;最详细的气象数据是具体到气象监测站点的数据&#xff01; 有关气象指标的监测站点数据&#xff0c;之前我们分享过1929-2023年全球气象站…

嵌入式开发——基础元器件

目录 1. 电阻 2. 电容 3. 电感 4. 二极管 5. 三极管 6. MOS管 7. 晶振 8. 磁珠 9. LDO 10. 电源 11. 接地 12. 线路 13. 电压表 14. 电流表 1. 电阻 根据欧姆定理&#xff0c;UI*R&#xff0c;通过某段导体的电流跟这段导体两端的电压成正比&#xff0c;跟这段导…

P6学习:解析P6 WBS-工作分解结构的原则

前言 WBS&#xff0c;及Work Breakdown Structure&#xff0c;中文工作分解结构&#xff0c;是总结工作阶段的项目的层次结构分解。 WBS 就像项目的大纲——它将项目分解为特定的可交付成果或阶段。 然后将活动添加到这些层中以创建项目计划的时间表。 WBS 使用流程会有所不…