c++ - unordered_set与unordered_map模拟实现

文章目录

  • 前言
    • 一、unordered_set模拟实现
    • 二、unordered_map模拟实现


前言

1、unordered_setunordered_map的介绍与接口使用可参考:unordered_set 、 unordered_map。
2、unordered_setunordered_map 的底层实现都是基于哈希表的。哈希表是一种通过哈希函数组织数据,以支持快速插入和搜索的数据结构。在 C++ STL(Standard Template Library)中,这两种容器利用哈希表的高效特性来提供快速的查找、插入和删除操作。
3、哈希表

namespace OpenHash
{//前置声明 -->给迭代器向上查找template<class K, class T, class KeyOfValue, class HF >class HashBucket;//哈希节点template<class T>struct HashBucketNode{HashBucketNode(const T& val): _pNext(nullptr), _val(val){}HashBucketNode<T>* _pNext;	T _val;	//数据};//迭代器template<class K,class T,class  KeyOfValue,class HF,class Ptr,class Ref>struct HsIterator{typedef HashBucketNode<T> Node;typedef HashBucket<K, T, KeyOfValue, HF> Hash;typedef HsIterator<K, T, KeyOfValue, HF,Ptr,Ref> Self;Node* _cur;const Hash* _hash;HsIterator(const  Hash* hash,  Node* cur ):_cur(cur),_hash(hash){}Self& operator++(){	KeyOfValue func;HF hf;if (_cur->_pNext != nullptr){_cur = _cur->_pNext;}else{int hashi = hf(func(_cur->_val)) % _hash->_table.size();Node* cur = _hash->_table[++hashi];while (hashi < _hash->_table.size() && cur == nullptr){cur = _hash->_table[hashi];hashi++;	}_cur = cur;}return *this;}bool operator==(const Self& it){return _cur == it._cur;}bool operator!= (const Self& it){return _cur != it._cur;}Ref operator*(){return _cur->_val;}Ptr operator->(){return &(_cur->_val);}}; template<class K,class T, class KeyOfValue, class HF >class HashBucket{typedef HashBucketNode<T> Node;	//节点重命名typedef HashBucket<K,T, KeyOfValue, HF> Self;	//自身重命名public:typedef  HsIterator<K, T, KeyOfValue, HF, T*, T&> Iterator;	//迭代器重命名typedef HsIterator<K, T, KeyOfValue, HF,const T*,const T&> ConstIterator;	//const迭代器重命名//迭代器作为有友元template<class K, class T, class  KeyOfValue, class HF, class Ptr, class Ref>friend struct HsIterator;//构造函数 哈希桶初始化大小为10HashBucket(size_t capacity = 10): _table(10), _size(0){}//拷贝构造HashBucket(const Self& self){_table.resize(10);//通过遍历Self和插入接口即可for (int i = 0; i < self._table.size(); i++){Node* cur = self._table[i];while (cur){Insert(cur->_val);cur = cur->_pNext;}}}//赋值重载Self& operator=(Self self){Clear();Swap(self);return *this;}~HashBucket(){Clear();}Iterator Begin(){//找到第一个不为空的桶int i = 0;while (_table[i] == nullptr){i++;}return Iterator(this, _table[i]);}Iterator End(){//用nullptr代表最后一个元素的后一个位置return Iterator(this, nullptr);}ConstIterator Begin() const{int i = 0;while (_table[i] == nullptr){i++;}return ConstIterator(this, _table[i]);}ConstIterator End() const{return ConstIterator(this, nullptr);}//返回值:为了unordered_map的重载[]能够使用pair<bool, Iterator>Insert(const T & val){//将数据转化为键值KeyOfValue func;//不允许重复键值Iterator it = Find(func(val));if ( it != End()){return make_pair(false,it);}//将键值转化为整形(哈希地址)HF ik;int hashi = ik(func(val)) % _table.size();//超过负载因子if (_size == _table.size()){//库容2倍vector<Node*> tmp(_table.size() * 2);//将原来的数据转移到tmpfor (int i = 0; i < _table.size(); i++){if (_table[i] != nullptr){Node* cur = _table[i];while (cur){//重新获取哈希地址int k = ik(func(cur->_val)) % tmp.size();//头插入tmpNode* next = cur->_pNext;cur->_pNext = tmp[k];tmp[k] = cur;cur = next;}}}//将容器进行交换_table.swap(tmp);}//插入Node* cur = new Node(val);cur->_pNext = _table[hashi];_table[hashi] = cur;_size++;return make_pair(true, Iterator(this,cur));}// 删除哈希桶中为data的元素(data不会重复)bool Erase(const K& key){//将数据转化为键值KeyOfValue func;//将数据转化为键值HF ik;int hashi = ik(key) % _table.size();//获取头节点Node* cur = _table[hashi];//前驱指针Node* prev = nullptr;while (cur){//找到,重新连接if (func(cur->_val) == key){if (prev == nullptr){_table[hashi] = cur->_pNext;}else{prev->_pNext = cur->_pNext;}_size--;delete cur;return true;}prev = cur;cur = cur->_pNext;}return false;}Iterator Find(const K& key){//将数据转化为键值KeyOfValue func;//将数据转化为键值HF ik;int hashi = ik(key) % _table.size();//获取头节点Node* cur = _table[hashi];//查找while (cur){if (func(cur->_val) == key){return Iterator(this,cur);}cur = cur->_pNext;}return Iterator(this, nullptr);}//大小size_t Size()const{return _size;}//是否为空bool Empty()const{return 0 == _size;}//清空void Clear(){//遍历容器清空每个节点for (int i = 0; i < _table.size(); i++){Node* cur = _table[i];while (cur){Node* next = cur->_pNext;delete cur;cur = next;}//最后置空_table[i] = nullptr;}//大小为0_size = 0;}//交换void Swap(Self& ht){//交换内部容器即可_table.swap(ht._table);swap(_size, ht._size);}private:vector<Node*> _table;	//储存哈希桶头节点指针size_t _size = 0;      // 哈希表中有效元素的个数};
};

一、unordered_set模拟实现

1、底层实现

unordered_set 的底层是一个哈希表。但是,它只存储元素本身,而不是键值对。因此,每个元素的值同时也是其唯一的键。

2、特点

无序性:元素的顺序不保证,且可能会随着插入和删除操作而改变。
元素的唯一性:unordered_set 中的每个元素都是唯一的。
快速查找:同样地,平均情况下,查找、插入和删除操作的时间复杂度都是 O(1)。

3、代码实现
(1)基础框架

//哈希函数
template<class	K>
class HashFunc
{
public:size_t operator()(const K& val){return val;}
};
//特化版本的哈希函数
template<>
class HashFunc<string>
{
public:size_t operator()(const string& s){const char* str = s.c_str();unsigned int seed = 131; // 31 131 1313 13131 131313unsigned int hash = 0;while (*str){hash = hash * seed + (*str++);}return hash;}
};//数据转化为键值的函数
template<class K>
struct KeyOfValue
{const K& operator()(const K& data){return data;}
};//unordered_set
template<class K, class HF = HashFunc<K>>
class unordered_set
{//哈希表重命名typedef OpenHash::HashBucket<K, const K, KeyOfValue<K>, HF> HT;
public://对哈希表迭代器进行重命名typename typedef  HT::Iterator iterator;typename typedef  HT::ConstIterator const_iterator;private://哈希表HT _ht;
};

unordered_set模板参数
K:即充当键值也充当数据
HF:哈希函数,用于计算哈希值

unordered_set成员变量
_ht: 哈希表

给哈希表传模板参数
K : 代表键值类型,该类型变量用于查找删除等接口
const K:代表数据类型,因为不能被修改所以加上const修饰,该类型变量用于插入等
KeyOfValue<K>:将数据转化为键值(在unordered_set中不明显,因为键值类型与数据类型一致,主要在unordered_map中体现)
HF:哈希函数,用于计算哈希值

(2)接口实现
这里的接口都是直接复用哈希表的接口,包括插入、删除、查找等,析构不用我们自己实现,因为结束时析构自动调用哈希表的析构函数进行清理。

//构造函数unordered_set() : _ht(){}//拷贝构造函数unordered_set(const unordered_set& p) :_ht(p._ht){}//赋值函数unordered_set& operator=(const unordered_set& p){_ht = p._ht;return *this;}//迭代器iterator begin(){return _ht.Begin();}iterator end(){return _ht.End();}const_iterator begin() const{return _ht.Begin();}const_iterator end()const{return _ht.End();}//元素个数size_t size()const { return _ht.Size(); }//是否为空bool empty()const { return _ht.Empty(); }//查找iterator find(const K& key) { return _ht.Find(key); }//插入pair<bool, iterator> insert(const K& valye){return _ht.Insert(valye);}//删除bool  erase(const K& position){return _ht.Erase(position);}

(3)测试

void test_set()
{//默认构造unordered_set<int> s;//插入s.insert(1);s.insert(2);s.insert(3);s.insert(4);cout << "s1:";//拷贝构造unordered_set<int> s1(s);//迭代器遍历unordered_set<int>::iterator it1 = s1.begin();while (it1 != s1.end()){cout << *it1 << " ";++it1;}cout << endl;cout << "s2:";//赋值unordered_set<int> s2;s2 = s;//删除s2.erase(1);s2.erase(2);//迭代器遍历unordered_set<int>::iterator it2 = s2.begin();while (it2 != s2.end()){cout << *it2 << " ";++it2;}
}

在这里插入图片描述

二、unordered_map模拟实现

1、底层实现

unordered_map 的底层是一个哈希表,每个元素都是一个键值对(key-value pair)。通过哈希函数,键(key)被映射到一个特定的位置(也称为桶或槽位),这个位置存储了相应的值(value)。如果多个键经过哈希函数计算后得到相同的位置,就发生了哈希冲突,此时通常通过链表或红黑树等数据结构来解决冲突。

2、特点

无序性:元素的顺序不保证,且可能会随着插入和删除操作而改变。
快速查找:平均情况下,查找、插入和删除操作的时间复杂度都是 O(1)。
键的唯一性:每个键在 unordered_map 中必须是唯一的。

3、代码实现
(1)基础框架

//哈希函数
template<class	K>
class HashFunc
{
public:size_t operator()(const K& val){return val;}
};
//特化string
template<>
class HashFunc<string>
{
public:size_t operator()(const string& s){const char* str = s.c_str();unsigned int seed = 131; unsigned int hash = 0;while (*str){hash = hash * seed + (*str++);}return hash;}
};//数据转化为键值
template<class K, class V>
struct KeyOfValue
{const K& operator()(const pair<K, V>& data){return data.first;}
};template<class K, class V, class HF = HashFunc<K>>
class unordered_map
{//对哈希表重命名typedef OpenHash::HashBucket<K, pair<const K, V>, KeyOfValue<K, V>, HF>  HT;public://迭代器重命名typename typedef  HT::Iterator iterator;typename typedef  HT::ConstIterator const_iterator;private://哈希表HT _ht;
};

unordered_map模板参数
K:键值类型
V:数据类型
HF:哈希函数

unordered_map成员变量
_ht:哈希表

给哈希表传模板参数
K:键值,用于删除、查找等接口
KeyOfValue<K, V>:将数据转化为键值
pair<const K, V>:作为数据,其中const K作为键值类型(因为键值不能改需要通过const修饰),V作为映射的数据类型,通过KeyOfValue<K, V> 函数返回键值(如在插入时只传入pair<const K, V>类型数据,无法通过数据进行比较,所以需要通过该函数转化)。
HF:哈希函数

(2)接口实现
A . 重载[ ] : 该接口的特点是如果该键值的不存在,则进行插入(键值映射的数据用默认构造初始化),如果存在就返回键值映射的数据,所以需要与插入接口的配合,通过插入接口的返回值来配合。

//重载[]
V& operator[](const K& key)
{pair < bool, iterator > ret = _ht.Insert(make_pair(key, V()));return ret.second->second;
}

B . 其他接口都是直接复用哈希表的接口,包括插入、删除、查找等,析构不用我们自己实现,因为结束时析构自动调用哈希表的析构函数进行清理。

//构造函数
unordered_map() : _ht()
{}//拷贝构造
unordered_map(const unordered_map& p) :_ht(p._ht)
{}//赋值
unordered_map & operator=(const unordered_map& p)
{_ht = p._ht;return *this;
}//迭代器
iterator begin()
{return _ht.Begin();
}
iterator end()
{return _ht.End();
}
const_iterator begin() const
{return _ht.Begin();
}
const_iterator end()const
{return _ht.End();
}//元素个数
size_t size()const
{ return _ht.Size();
}
//是否为空
bool empty()const 
{ return _ht.Empty();
}//查找
iterator find(const K& key) 
{ return _ht.Find(key);
}
//
 插入
pair<bool,iterator> insert(const pair<K, V>& valye)
{return _ht.Insert(valye);
}//删除
bool erase(const K&  key)
{return _ht.Erase(key);
}

(3)测试

 void test_map()
{//默认构造unordered_map<int, int> m;//插入m.insert({ 1,1 });m.insert({ 2,2 });m.insert({ 3,3 });m.insert({ 4,4 });cout << "m1:";//拷贝构造unordered_map<int, int> m1 = m;//迭代器遍历unordered_map<int, int>::iterator it1 = m1.begin();while (it1 != m1.end()){cout << it1->first << ":" << it1->second << "   ";++it1;}cout << endl;cout << "m2:";//赋值unordered_map<int, int> m2;m2 = m;//删除m2.erase(1);m2.erase(2);//重载[]m2[3] = 100;m2[5] = 100;//迭代器遍历unordered_map<int, int>::iterator it2 = m2.begin();while (it2 != m2.end()){cout << it2->first << ":" << it2->second << "   ";++it2;}
}

在这里插入图片描述

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

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

相关文章

LoRa无线通讯,让光伏机器人实现无“线”管理

光伏清洁机器人&#xff0c;作为光伏电站运维的新兴关键设备&#xff0c;已跃升为继组件、支架、光伏逆变器之后的第四大核心组件&#xff0c;正逐步成为光伏电站的标准配置。鉴于光伏电站普遍坐落于偏远无人区或地形复杂之地&#xff0c;光伏清洁机器人必须具备远程操控能力、…

深入源码P3C-PMD:rule (4)

系列文章目录 文章目录 系列文章目录rule 的应用类别 rule rule 自定义XML rule 定义Tree 漫游错误报告生命周期 designer rule相关的代码在每个子 module 的 rule 文件夹。而且也以一些 ruleset 为范围分了文件夹&#xff0c;如下图所示&#xff1a; 对每个 rule 来说&#xf…

PHP教育培训小程序系统源码

&#x1f680;【学习新纪元】解锁教育培训小程序的无限可能✨ &#x1f4da; 引言&#xff1a;教育培训新风尚&#xff0c;小程序来引领&#xff01; Hey小伙伴们&#xff0c;是不是还在为找不到合适的学习资源而烦恼&#xff1f;或是厌倦了传统教育模式的单调&#xff1f;今…

盘点12款企业常用源代码加密软件,源代码防泄密很重要!

在当今的商业环境中&#xff0c;源代码作为企业的核心资产之一&#xff0c;其安全性不容忽视。源代码的泄露可能导致企业丧失竞争优势、面临法律诉讼甚至经济损失。因此&#xff0c;选择合适的源代码加密软件成为企业保护知识产权和核心技术的关键步骤。 1. 安秉源代码加密软件…

【JVM】Java内存区域图文详解

1.JVM运行时区域总览 Java 虚拟机在执行 Java 程序的过程中会把它管理的内存划分成若干个不同的数据区域。 JVM运行时区域也成为Java内存区域。 在讨论Java内存模型时&#xff0c;通常将其分为线程共享区域和线程私有区域&#xff1a; 2.线程私有区域 2.1.程序计数器 程序计…

详解贪心算法

贪心算法&#xff08;Greedy Algorithm) 概述&#xff1a; 贪心算法是一种在求解最优化问题时采取的一种常用算法策略。贪心算法的基本思想是&#xff0c;每次选择当前情况下的局部最优解&#xff0c;并相信这个局部最优解能够导致全局最优解。贪心算法通过迭代的方式一步步地…

SpringBoot3里的文件上传

需求分析&#xff1a; 在用户更换头像或者添加文章时&#xff0c;都需要携带一个图片的URL访问地址。当用户访问文件上传接口将图片的数据上传成功后&#xff0c;服务器会返回一个地址。我们后台需要提供一个文件上传的接口&#xff0c;用来接收前端提交的文件的数据并且返回文…

七夕情人节礼物有哪些走心礼物推荐,盘点惊喜爆棚的四大礼物分享

随着浪漫的七夕情人节的临近&#xff0c;恋人们开始寻找那些能够传达他们挚爱与深情的独特礼物&#xff0c;一份有心的礼物不仅能够成为情感的使者&#xff0c;还能在这一天为爱情增添更多甜蜜与回忆&#xff0c;在这个充满传说与浪漫的节日里&#xff0c;我们精心挑选了一系列…

查询表信息时有一个数据为null相关解决

查询的时候varchar类型的username一直查不到为null,这个问题干了我好久 当时我以为是连接mysql数据库的时候没有在url后面添加添加指定字符的编码、解码格式的参数约束.然后经过分析发现 我创建的这个Account对象 直接上结果&#xff0c;问题出在了setUsername()方法上 错误…

Scrapy入门篇

免责声明 本文的爬虫知识仅用于合法和合理的数据收集&#xff0c;使用者需遵守相关法律法规及目标网站的爬取规则&#xff0c;尊重数据隐私&#xff0c;合理设置访问频率&#xff0c;不得用于非法目的或侵犯他人权益。因使用网络爬虫产生的任何法律纠纷或损失&#xff0c;由使用…

用Java手写jvm之模拟方法调用指令invokexxx和方法返回指令xreturn

写在前面 源码 。 本文一起看下方法调用相关的指令invokexxx以及方法返回&#xff08;栈帧弹出线程栈&#xff09;相关的指令xReturn 。 1&#xff1a;正文 因为invokexxx指令和普通的指令不同&#xff0c;会创建一个新的栈帧&#xff0c;并压倒操作数栈中&#xff0c;所以我…

【python】Python中实现定时任务常见的几种方式原理分析与应用实战

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

【OpenCV C++20 学习笔记】腐蚀和膨胀

腐蚀和膨胀 形态学原理膨胀腐蚀 代码实现膨胀函数腐蚀函数运行结果 形态学原理 腐蚀和膨胀通常有以下用途&#xff1a; 去除噪音分离或合并图像中的元素找出图片上的强度的极大值区域和极小值区域 以下图作为原始图片&#xff1a; 膨胀 用核 B B B来扫描图像 A A A&#xff…

VBA学习(22):动态显示日历

这是在ozgrid.com论坛上看到的一个贴子&#xff0c;很有意思&#xff0c;本来使用公式是可以很方便在工作表中实现日历显示的&#xff0c;但提问者因其需要&#xff0c;想使用VBA实现动态显示日历&#xff0c;即根据输入的年份和月份在工作表中显示日历。 下面是实现该效果的VB…

喜报!DAP-seq文章6连发,总IF 95.2

2024年4月29日&#xff0c;河北农业大学林学院李保国山区产业开发与林果产业创新团队与园艺学院田义教授团队联合西北农林科技大学马锋旺教授团队及沈阳农业大学马跃教授团队在Plant Biotechnology Journal&#xff08;影响因子10.1&#xff09;上发表了题为“The MdVQ37-MdWRK…

2024最新版Python基础入门学习路线

Python基础入门学习路线可以概括为以下几个阶段&#xff0c;每个阶段都包含了关键的学习内容和目标&#xff1a; 一、Python语言基础 1. 初识Python语言 Python语言概述&#xff1a;了解Python的起源、特点、应用领域以及发展趋势。环境安装&#xff1a;学习如何在不同的操作系…

18987 随机数(测验)

这个问题可以通过使用集合&#xff08;set&#xff09;和排序来解决。集合是一种数据结构&#xff0c;它可以自动去除重复的元素。然后我们可以将集合中的元素转移到一个数组中&#xff0c;并对&#xfffd;&#xfffd;组进行排序。 以下是使用C的代码实现&#xff1a; #i…

浅谈哈希与哈希表(c++)

目录 一、哈希的基本概念&#xff08;一&#xff09;哈希函数的特性&#xff08;二&#xff09;哈希冲突 二、C 中的哈希表实现三、哈希表的性能分析四、哈希表的应用场景五、优化哈希表的策略六、例题讲解【模板】字符串哈希题目描述输入格式输出格式样例 #1样例输入 #1样例输…

工业5G路由器赋能户外组网远程监控及预警

随着物联网、大数据、云计算等技术的快速发展&#xff0c;工业领域对于远程监控、实时预警和数据传输的需求日益增长。特别是在户外复杂环境下&#xff0c;传统的有线网络组网方式面临着布线难度大、成本高、维护困难等问题。 工业5G路由器在户外组网远程监控预警应用基于高速…

Android开发之事件分发

#来自ウルトラマンゼロ&#xff08;哉阿斯&#xff09; 1 Activity 构成 平常布局展示在ContentView中。 2 事件分发 事件分发的本质其实就是把事件&#xff08;Touch&#xff09;封装成 MotionEvent 类&#xff0c;然后传递给 View 的层级处理。 MotionEvent 事件类型主要有…