【C++】深入哈希表核心:从改造到封装,解锁 unordered_set 与 unordered_map 的终极奥义!

文章目录

  • 修改哈希表
    • 模板参数
    • 迭代器
    • ``HashTable`` 的默认成员函数
    • ``HashTable`` 迭代器相关函数
    • ``HashTable`` 的 ``Insert`` 函数
    • ``HashTable`` 的 ``Find``函数
    • ``HashTable`` 的 ``Erase``函数
  • 封装 unordered_set
  • 封装 unordered_map
  • 测试 unordered_set 和 unordered_map


修改哈希表

我们基于链地址法实现的哈希表来封装实现 unordered_setunordered_map ,但是由于实现的哈希表是 Key-Value 结构的并且我们的实现的哈希表缺少了迭代器,所以我们需要对之前实现的哈希表进行改造。

模板参数

HashNode
节点里不再存储确定的 pair<K, V> ,而是类型 T ,代表代表存储的数据可能是 key 或者 key-Value

template<class T>
struct HashNode
{T _data;HashNode<T>* _next;HashNode(const T& data):_data(data), _next(nullptr){}
};

HashTable

template<class K, class T, class KeyOfT, class hash = HashFunc<K>>
class HashTable
  • K :代表的是 key
  • T :代表是存的可能是 key 或者 key-value
  • KeyOfT :仿函数,目的是拿到 T 里面的 key

迭代器

哈希表的迭代器其实就是对节点指针进行封装,而且是单向迭代器,只需实现 ++ 即可。

//哈希表与迭代器相互依赖,需要前置声明
template<class K, class T, class KeyOfT, class hash>
class HashTable;template<class K, class T, class KeyOfT, class Hash, class Ref, class Ptr>
struct HTIterator
{typedef HashNode<T> Node;typedef HashTable<K, T, KeyOfT, Hash> HT;typedef HTIterator<K, T, KeyOfT, Hash, Ref, Ptr> Self;Node* _node;//需要用到哈希表里的数组大小,故需要指向哈希表的指针const HT* _pht;HTIterator(Node* node, const HT* pht):_node(node),_pht(pht){}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}bool operator!=(const Self& s){return _node != s._node;}Self& operator++(){//...} 
};

由于哈希表的特殊性,其迭代器的 ++ 较为复杂,也是实现哈希表迭代器的重点。
重载 ++
迭代器的++分为两种情况:

  • 当前迭代器不是哈希桶的最后一个节点,直接走到下一个节点。
  • 当前迭代器是哈希桶的最后一个节点,需要找下一个不为空的哈希桶的头节点。
    在这里插入图片描述
    在这里插入图片描述
Self& operator++()
{//当前哈希桶还有数据,直接到下一个节点if (_node->_next){_node = _node->_next;}//当前哈希桶走完了,寻找下一个不为空的哈希桶else if (_node->_next == nullptr){KeyOfT kot;Hash hashfunc;size_t hashi = hashfunc(kot(_node->_data)) % _pht->_tables.size();++hashi;while (hashi < _pht->_tables.size()){_node = _pht->_tables[hashi];//找到了下一个桶if (_node)break;else++hashi;}//处理迭代器在最后一个不为空的哈希桶的最后一个节点的情况if (hashi == _pht->_tables.size()){_node = nullptr;}}return *this;
}Self& operator++(int)
{Self tmp = *this;++(*this);return tmp;
}

HashTable 的默认成员函数

//构造函数
HashTable(): _tables(__stl_next_prime(0)), _n(0)
{}//拷贝构造函数
HashTable(const HashTable<K, T, KeyOfT, hash>& ht)
{_tables.resize(ht._tables.size());for (auto& data : ht){Insert(data);}
}//赋值重载
HashTable<K, T, KeyOfT, hash>& operator=(const HashTable<K, T, KeyOfT, hash>& ht)
{vector<Node*> newtables(ht._tables.size());for (size_t i = 0; i < ht._tables.size(); ++i){Node* cur = ht._tables[i];while (cur){Node* newnode = new Node(cur->_data);newnode->_next = newtables[i];newtables[i] = newnode;cur = cur->_next;}}_tables.swap(newtables);return *this;
}
//析构函数
~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;}
}

HashTable 迭代器相关函数

typedef HTIterator<K, T, KeyOfT, hash, T&, T*> Iterator;
typedef HTIterator<K, T, KeyOfT, hash, const T&, const T*> Const_Iterator;Iterator begin()
{if (_n == 0)return end();for (size_t i = 0; i < _tables.size(); ++i){Node* cur = _tables[i];if (cur){return Iterator(cur, this);}}return end();
}Iterator end()
{return Iterator(nullptr, this);
}Const_Iterator begin() const
{if (_n == 0)return end();for (size_t i = 0; i < _tables.size(); ++i){Node* cur = _tables[i];if (cur){return Const_Iterator(cur, this);}}}Const_Iterator end() const
{return Const_Iterator(nullptr, this);
}

begin() 对应的迭代器应该是哈希表中第一个非空的哈希桶的头节点,需要特别处理。
end() 直接返回空指针对应的迭代器即可。

HashTableInsert 函数

对于 Insert 函数,只需将其返回值改成迭代器布尔值pair ,再将原本关于使用到 key 的地方套一层 KeyOfT 实例化出来的对象即可。

void CheckCapacity()
{if (_n == _tables.size()){//把哈od希桶里的链表每个节点拆下来插入newht效率太低了/*HashTable<K, V> newht;newht._tables.resize(__stl_next_prime(_tables.size() + 1));for (size_t i = 0; i < _tables.size(); ++i){Node* cur = _tables[i];while (cur){newht.Insert(cur->_kv);cur = cur->_next;}}_tables.swap(newht._tables);*/KeyOfT kot;hash hashfunc;vector<Node*> newtables(__stl_next_prime(_tables.size() + 1));for (size_t i = 0; i < _tables.size(); ++i){Node* cur = _tables[i];while (cur){Node* next = cur->_next;size_t hashi = hashfunc(kot(cur->_data)) % newtables.size();//头插cur->_next = newtables[hashi];newtables[hashi] = cur;cur = next;}_tables[i] = nullptr;}_tables.swap(newtables);}
}pair<Iterator, bool> Insert(const T& data)
{KeyOfT kot;Iterator it = Find(kot(data));//不等于end()说明哈希表里有重复元素if (it != end())return { it, false };//检查是否需要扩容CheckCapacity();hash hashfunc;size_t hashi = hashfunc(kot(data)) % _tables.size();Node* newnode = new Node(data);newnode->_next = _tables[hashi];_tables[hashi] = newnode;++_n;return { Iterator(newnode, this), false };
}

HashTableFind函数

将返回值改为迭代器,原本用到 key 的地方套一层 KeyOfT 实例化出来的对象即可。

Iterator Find(const K& key)
{KeyOfT kot;hash hashfunc;size_t hashi = hashfunc(key) % _tables.size();Node* cur = _tables[hashi];while (cur){if (kot(cur->_data) == key){return Iterator(cur, this);}cur = cur->_next;}//没找到就返回return end();
}

HashTableErase函数

Insert 函数的处理基本一样,不多叙述了。

bool Erase(const K& key)
{KeyOfT kot;hash hashfunc;size_t hashi = hashfunc(key) % _tables.size();Node* prev = nullptr;Node* cur = _tables[hashi];while (cur){if (kot(cur->_data) == key){//删除头节点的情况if (prev == nullptr){_tables[hashi] = cur->_next;}//非头节点的情况else{prev->_next = cur->_next;}delete cur;--_n;return true;}else {prev = cur;cur = cur->_next;}}return false;
}

封装 unordered_set

HashTable 的迭代器和函数进行封装即可,灰常简单。

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;typedef typename hash_bucket::HashTable<K, const K, SetKeyOfT, 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);}iterator Find(const K& key){return _ht.Find(key);}bool Erase(const K& key){return _ht.Erase(key);}private:hash_bucket::HashTable<K, const K, SetKeyOfT, hash> _ht;
};

封装 unordered_map

也是对 HashTable 的迭代器和函数进行封装,但有所不同的是,还需要多重载 [] ,也比较简单。

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;typedef typename hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT, 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);}V& operator[](const K& key){pair<iterator, bool> ret = Insert({ key, V() });return ret.first->second;}iterator Find(const K& key){return _ht.Find(key);}bool Erase(const K& key){return _ht.Erase(key);}private:hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT, hash> _ht;
};

测试 unordered_set 和 unordered_map

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


拜拜,下期再见😏

摸鱼ing😴✨🎞
请添加图片描述
我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=3kzw99ffumyoo

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

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

相关文章

TEA加密逆向

IDA伪代码 do{if ( v15 )v17 v38; // x120x0->0x79168ba790&#xff0c; 输入字符串经过check1处理后字符串elsev17 v40;v18 (unsigned int *)&v17[v16]; // 0x78cbbd47fc add x12, x12, x8 ; x120x79168ba790->…

android 性能分析工具(03)Android Studio Profiler及常见性能图表解读

说明&#xff1a;主要解读Android Studio Profiler 和 常见性能图表。 Android Studio的Profiler工具是一套功能强大的性能分析工具集&#xff0c;它可以帮助开发者实时监控和分析应用的性能&#xff0c;包括CPU使用率、内存使用、网络活动和能耗等多个方面。以下是对Android …

【FPGA】Verilog:利用 4 个串行输入- 串行输出的 D 触发器实现 Shift_register

0x00 什么是寄存器 寄存器(Register)是顺序逻辑电路中使用的基本组成部分之一。寄存器用于在数字系统中存储和处理数据。寄存器通常由位(bit)构成,每个位可以存储一个0或1的值。通过寄存器,可以设计出计数器、加法器等各种数据处理电路。 0x01 寄存器的种类 基于 D 触发…

用 Python 从零开始创建神经网络(十):优化器(Optimizers)(持续更新中...)

优化器&#xff08;Optimizers&#xff09; 引言1. 随机梯度下降/Stochastic Gradient Descent (SGD)2. 学习率&#xff08;Learning Rate&#xff09;3. 学习率衰减&#xff08;Learning Rate Decay&#xff09;4. 带动量的随机梯度下降法&#xff08;Stochastic Gradient Des…

鱼眼相机模型-MEI

参考文献&#xff1a; Single View Point Omnidirectional Camera Calibration from Planar Grids 1. 相机模型如下&#xff1a; // 相机坐标系下的点投影到畸变图像// 输入&#xff1a;相机坐标系点坐标cam 输出&#xff1a; 畸变图像素点坐标disPtvoid FisheyeCamAdapter::…

Spring Boot 实战:基于 Validation 注解实现分层数据校验与校验异常拦截器统一返回处理

1. 概述 本文介绍了在spring boot框架下&#xff0c;使用validation数据校验注解&#xff0c;针对不同请求链接的前端传参数据&#xff0c;进行分层视图对象的校验&#xff0c;并通过配置全局异常处理器捕获传参校验失败异常&#xff0c;自动返回校验出错的异常数据。 2. 依赖…

20241125复盘日记

昨日最票&#xff1a; 南京化纤 滨海能源 广博股份 日播时尚 众源新材 返利科技 六国化工 丰华股份 威领股份 凯撒旅业 华扬联众 泰坦股份 高乐股份高均线选股&#xff1a; 理邦仪器高乐股份日播时尚领湃科技威领股份资金最多的票&#xff1a; 资金攻击最多的票&#xff1a; …

STM32WB55RG开发(5)----监测STM32WB连接状态

STM32WB55RG开发----5.生成 BLE 程序连接手机APP 概述硬件准备视频教学样品申请源码下载参考程序选择芯片型号配置时钟源配置时钟树RTC时钟配置RF wakeup时钟配置查看开启STM32_WPAN条件配置HSEM配置IPCC配置RTC启动RF开启蓝牙LED配置设置工程信息工程文件设置参考文档SVCCTL_A…

游戏引擎学习第23天

实时代码编辑功能的回顾 当前实现的实时代码编辑功能已经取得了显著的成功&#xff0c;表现出强大的性能和即时反馈能力。该功能允许开发者在修改代码后几乎立即看到变化在运行中的程序中体现出来&#xff0c;极大提升了开发效率。尽管目前的演示内容较为简单&#xff0c;呈现…

ARM CCA机密计算安全模型之概述

安全之安全(security)博客目录导读 目录 1、CCA的要素 2、CCA平台 2.1 CCA 系统安全域 2.2 监控安全域 2.3 领域管理安全域 3、与系统平台安全服务的关系 3.1 安全配置 3.2 平台认证 1、CCA的要素 高层次的 CCA 架构如下图中概述。 在硬件层面,CCA 系统安全域包括可…

2024 java大厂面试复习总结(一)(持续更新)

10年java程序员&#xff0c;2024年正好35岁&#xff0c;2024年11月公司裁员&#xff0c;记录自己找工作时候复习的一些要点。 java基础 hashCode()与equals()的相关规定 如果两个对象相等&#xff0c;则hashcode一定也是相同的两个对象相等&#xff0c;对两个对象分别调用eq…

【R语言管理】Pycharm配置R语言及使用Anaconda管理R语言虚拟环境

目录 使用Anaconda创建R语言虚拟环境1. 安装Anaconda2. 创建R语言虚拟环境 Pycharm配置R语言1. 安装Pycharm2. R Language for IntelliJ插件 参考 使用Anaconda创建R语言虚拟环境 1. 安装Anaconda Anaconda的安装可参见另一博客-【Python环境管理工具】Anaconda安装及使用教程…

系统设计时应时刻考虑设计模式基础原则

目录 :star2:单一职责原则 (Single Responsibility Principle, SRP):star2:开放-封闭原则 (Open-Closed Principle, OCP):star2:依赖倒转原则 (Dependency Inversion Principle, DIP):star2:里氏代换原则 (Liskov Substitution Principle, LSP):star2:迪米特原则 (Law of Demet…

Spring 中的 ProxyFactory 创建代理对象

一、jdk 动态代理 和 cglib动态代理 简单介绍 1.jdk动态代理 public interface AService {public String serviceA(String param);public String serviceAA(String param); } public interface BService {public String serviceB(String param);public String serviceBB(Str…

FreeRTOS之链表源码分析

文章目录 前言一、结构体1、链表List_t2、链表项xLIST_ITEM3、头节点xMINI_LIST_ITEM4、链表示意图 二、函数分析1、初始化函数vListInitialise2、初始化链表项vListInitialiseItem3、链表尾部添加节点vListInsertEnd4、按序插入节点vListInsert5、删除节点uxListRemove 总结 前…

【深度学习】【RKNN】【C++】模型转化、环境搭建以及模型部署的详细教程

【深度学习】【RKNN】【C】模型转化、环境搭建以及模型部署的详细教程 提示:博主取舍了很多大佬的博文并亲测有效,分享笔记邀大家共同学习讨论 文章目录 【深度学习】【RKNN】【C】模型转化、环境搭建以及模型部署的详细教程前言模型转换--pytorch转rknnpytorch转onnxonnx转rkn…

Matlab 深度学习工具箱 案例学习与测试————求二阶微分方程

clc clear% 定义输入变量 x linspace(0,2,10000);% 定义网络的层参数 inputSize 1; layers [featureInputLayer(inputSize,Normalization"none")fullyConnectedLayer(10)sigmoidLayerfullyConnectedLayer(1)sigmoidLayer]; % 创建网络 net dlnetwork(layers);% 训…

51单片机-独立按键与数码管联动

独立键盘和矩阵键盘检测原理及实现 键盘的分类&#xff1a;编码键盘和非编码键盘 键盘上闭合键的识别由专用的硬件编码器实现&#xff0c;并产生键编码号或键值的称为编码键盘&#xff0c;如&#xff1a;计算机键盘。靠软件编程识别的称为非编码键盘&#xff1b;在单片机组成…

华为无线AC+AP组网实际应用小结

之前公司都是使用的H3C的交换机、防火墙以及无线AC和AP的&#xff0c;最近优化下无线网络&#xff0c;说新的设备用华为的&#xff0c;然后我是直到要部署的当天才知道用华为设备的&#xff0c;就很无语了&#xff0c;一点准备没有&#xff0c;以下为这次的实际操作记录吧&…

浅谈网络 | 传输层之TCP协议

目录 TCP 包头格式TCP 的三次握手TCP 的四次挥手TCP 的可靠性与"靠谱"的哲学TCP流量控制TCP拥塞控制 上一章我们提到&#xff0c;UDP 就像我们小时候一样简单天真&#xff0c;它相信“网之初&#xff0c;性本善&#xff0c;不丢包&#xff0c;不乱序”&#xff0c;因…