从零带你底层实现unordered_map (2)

💯 博客内容:从零带你实现unordered_map

😀 作  者:陈大大陈

🚀 个人简介:一个正在努力学技术的准C++后端工程师,专注基础和实战分享 ,欢迎私信!

💖 欢迎大家:这里是CSDN,我总结知识和写笔记的地方,喜欢的话请三连,有问题请私信 😘 

目录

闭散列/哈希桶 拉链法

开散列图示: 

开散列代码: 

增容代码:


哈希/散列:映射,关键字和另一个值建立一个关联关系。

哈希表/散列表:映射,关键字和储存位置建立一个关联关系。

哈希/散列是一种算法思想,而哈希表/散列表是基于这种算法思想而实现的一种数据结构,这点很容易混淆。

上一篇博客介绍了两个解决哈希冲突的方法,

1.线性探测  hashi+i (i>=0)

2.二次探测  hashi+i^2 (i>=0)

这两种方法都不算是什么灵丹妙药,还是太慢。

最好的方法是下面这个。

闭散列/哈希桶 拉链法

哈希每一个存的不是唯一的值,而是一个指针数组。

这样一来,key值相同的值都会存到一个指针数组里面,查找就方便了很多。

它的查找直接‘’内部消化‘’,不会影响到别的值。

这样的每一个节点,我们称之为桶。

当一个桶的节点过多时吗,这个桶的存储结构由链表变为红黑树。

平均时间复杂度是O(1)。

当存储的值是string等类型的话,不能直接入表。

要使用仿函数来类型转换。

HashFunc的作用是转成整型值。

直接把字母的ASCII值加起来看行不行。

需要特别注意的是,汉字的ASCII值是负数,存储的时候需要用到特殊的方法。

否则会发生整形提升,简单的两个汉字加起来就能有好几亿。

上篇文章也说过了:

 解决哈希冲突 两种常见的方法是:闭散列和开散列

闭散列,也叫开放定址法,当发生哈希冲突时,如果哈希表未被装满,说明在哈希表中必然还有

空位置,那么可以把key存放到冲突位置中的“下一个” 空位置中去。

今天咱们就来提提开散列。

开散列法又叫链地址法(开链法),首先对关键码集合用散列函数计算散列地址,具有相同地

址的关键码归于同一子集合,每一个子集合称为一个桶,各个桶中的元素通过一个单链表链

接起来,各链表的头结点存储在哈希表中。  

开散列图示: 

从上图可以看出,开散列中每一个桶中放的元素都是发生哈希冲突的。

开散列代码: 

#define _CRT_SECURE_NO_WARNINGS
template<class V>
struct HashBucketNode
{HashBucketNode(const V& data): _pNext(nullptr), _data(data){}HashBucketNode<V>* _pNext;V _data;
};
template<class V>
class HashBucket
{typedef HashBucketNode<V> Node;typedef Node* PNode;
public:HashBucket(size_t capacity = 3) : _size(0){_ht.resize(GetNextPrime(capacity), nullptr);}// 哈希桶中的元素不能重复PNode* Insert(const V& data){// 确认是否需要扩容。。。// _CheckCapacity();// 1. 计算元素所在的桶号size_t bucketNo = HashFunc(data);// 2. 检测该元素是否在桶中PNode pCur = _ht[bucketNo];while (pCur){if (pCur->_data == data)return pCur;pCur = pCur->_pNext;}// 3. 插入新元素pCur = new Node(data);pCur->_pNext = _ht[bucketNo];_ht[bucketNo] = pCur;_size++;return pCur;}// 删除哈希桶中为data的元素(data不会重复),返回删除元素的下一个节点PNode* Erase(const V& data){size_t bucketNo = HashFunc(data);PNode pCur = _ht[bucketNo];PNode pPrev = nullptr, pRet = nullptr;while (pCur){if (pCur->_data == data){if (pCur == _ht[bucketNo])_ht[bucketNo] = pCur->_pNext;elsepPrev->_pNext = pCur->_pNext;pRet = pCur->_pNext;delete pCur;_size--;return pRet;}}return nullptr;}PNode* Find(const V& data);size_t Size()const;bool Empty()const;void Clear();bool BucketCount()const;void Swap(HashBucket<V, HF>& ht;~HashBucket();
private:size_t HashFunc(const V& data){return data % _ht.capacity();}
private:vector<PNode*> _ht;size_t _size;      //哈希表中有效元素的个数
};

桶的个数是一定的,随着元素的不断插入,每个桶中元素的个数不断增多,极端情况下,可

能会导致一个桶中链表节点非常多,会影响的哈希表的性能,因此在一定条件下需要对哈希

表进行增容,那该条件怎么确认呢?开散列最好的情况是:每个哈希桶中刚好挂一个节点,

再继续插入元素时,每一次都会发生哈希冲突,因此,在元素个数刚好等于桶的个数时,可

以给哈希表增容。

增容代码:

void _CheckCapacity()
{size_t bucketCount = BucketCount();if(_size == bucketCount){HashBucket<V, HF> newHt(bucketCount);for(size_t bucketIdx = 0; bucketIdx < bucketCount; ++bucketIdx){PNode pCur = _ht[bucketIdx];while(pCur){// 将该节点从原哈希表中拆出来_ht[bucketIdx] = pCur->_pNext;// 将该节点插入到新哈希表中size_t bucketNo = newHt.HashFunc(pCur->_data);pCur->_pNext = newHt._ht[bucketNo];newHt._ht[bucketNo] = pCur;pCur = _ht[bucketIdx];}}newHt._size = _size;this->Swap(newHt);}
}

这块东西实在是太多,下篇博客咱们继续实现。

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

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

相关文章

figma 基础使用 —— 常用方法

一、 导入组件 分成两种方式 &#xff08;1&#xff09;离线的包导入&#xff08;iOS 常用组件.fig 直接拖拽到figma最近网页&#xff09; &#xff08;2&#xff09;在插件市场下载https://www.figma.com/community 二、figma中使用标尺 快捷键&#xff1a;shift R 三、插…

备案小技能:ICP备案(网站、app、小程序)经营性ICP备案(增值电信业务经营许可证)

文章目录 引言I ICP 备案1.1 小程序备案1.2 app备案1.3 网站备案1.4 公安备案II 经营性ICP备案2.1 增值电信业务经营许可证see also引言 一个阿里云账号,只能使用一个ICP备案主体,可以办理app和网站备案。 经营性ICP备案阿里云需要收取备案代理费用。福建需要产品价格¥414…

宿主Linux——KVM安装Windows7系统

KVM虚拟技术 KVM(Kernel-based Virtual Machine) 是基于Linux内核的开源虚拟化技术&#xff0c;在一台物理机上可同时运行多个虚拟系统。KVM使用硬件虚拟化扩展&#xff0c;例如Intel的VT和AMD的AMD-V&#xff0c;在性能方面更加高效&#xff0c;可提供更好的计算能力和响应速…

【从浅识到熟知Linux】基本指令之rmdir和rm

&#x1f388;归属专栏&#xff1a;从浅学到熟知Linux &#x1f697;个人主页&#xff1a;Jammingpro &#x1f41f;每日一句&#xff1a;加油努力&#xff0c;这次写完真的真的真的要去干饭了&#xff01; 文章前言&#xff1a;本文介绍rmdir和rm指令用法并给出示例和截图。 文…

存在即合理,低代码的探索之路

目录 一、前言 二、低代码迅速流行的原因 三、稳定性和生产率的最佳实践 四、程序员用低代码开发应用有哪些益处&#xff1f; 1、提升开发价值 2、利于团队升级 一、前言 低代码的热潮至今未消停&#xff0c;从阿里钉钉跨平台协作方式&#xff0c;再到飞书上的审批流程&#xf…

python getattr() setattr() hasattr() delattr()内置函数详解

python getattr() setattr() hasattr() delattr()内置函数详解 1.getattr(object, name[, default])&#xff1a; 功能&#xff1a;获取对象的属性值。 参数&#xff1a; object&#xff1a;要获取属性的对象。 name&#xff1a;属性的名称。 default&#xff08;可选&#x…

房屋租赁出售经纪人入驻小程序平台

一款专为房屋中介开发的小程序平台&#xff0c;支持独立部署&#xff0c;源码交付&#xff0c;数据安全无忧。 核心功能&#xff1a;房屋出租、经纪人独立后台、分佣后台、楼盘展示、房型展示、在线咨询、地址位置配套设施展示。 程序已被很多房屋交易中介体验使用过&#x…

hive两张表实现like模糊匹配关联

testa表(字段a)aaabbacccddddddaaatestb表(字段b)ab1. 使用likeconcat模糊配对 selecta.a from testa a ,testb b where a like concat(%,b.b,%) group by a.a2. 使用locate函数 selecta.a from testa a ,testb b where locate(b.b,a.a)>0 group by a.a3. 使用instr函数 sel…

uni-app 离线打包安卓Apk(小白上手)

场景&#xff1a; 在使用uni-app 开发apk时&#xff0c;使用云打包有次数限制。尤其对于测试阶段是无比难受的&#xff0c;通常是浪费打包次数进行打包或者通过usb 给测试机更新开发环境&#xff0c;但这都是无比漫长的过程 尤其有多个测试机真的是噩梦般的存在 下载离线打包示…

CMake中的变量: 与编程语言相关的变量

变量名称描述CMAKE_C_COMPILE_FEATURESC编译器已知的功能列表CMAKE_C_EXTENSIONSC_EXTENSIONS目标属性的默认值&#xff08;如果在创建目标时设置&#xff09;。CMAKE_C_STANDARDC_STANDARD目标属性的默认值&#xff08;如果在创建目标时设置&#xff09;。CMAKE_C_STANDARD_RE…

Android frameworks 开发总结之十一

1.查看android关机前的log 有时候我们在没有连接电脑的情况下,会在测试的时候突然机器关机. 这个时候如果查看 log信息就看不到了。测试前可以执行下面的命令,之后再进行测试. $ adb shell $ nohup logcat > /sdcard/xxx.log 2.android日期时间同步 关于android请求…

【数据库】聊聊一颗B+树 可以存储多少数据

我们知道数据库使用的数据结构是B树&#xff0c;但是B树可以存储多少数据呢&#xff0c;在面试中也是经常会问的问题&#xff0c;所以我们从根上理解这个问题。 操作系统层面 数据都是存储在磁盘中的&#xff0c;而磁盘中的数据都是以最新单位扇区进行分割。一个扇区的大小是…

Python基础语法之学习数据转换

Python基础语法之学习数据转换 一、代码二、效果 一、代码 #数字转换成字符串 num_str str(11) print(type(num_str))#字符串转整数 numint("11") print(type(num),num)#浮点数转整数 float_num int(11.1) print(type(float_num),float_num)#整数转浮点数 num_flo…

OpenSSL 使用AES对文件加解密

AES&#xff08;Advanced Encryption Standard&#xff09;是一种对称加密算法&#xff0c;它是目前广泛使用的加密算法之一。AES算法是由美国国家标准与技术研究院&#xff08;NIST&#xff09;于2001年发布的&#xff0c;它取代了原先的DES&#xff08;Data Encryption Stand…

webpack项目工程初始化

一、初始化项目 默认系统已经安装node //初始化 pnpm init//安装webpack pnpm i -D webpack webpack-cli 新建一个index.html的入口文件 新建一个src文件存放js代码&#xff0c;src里面新建一个index.js package.josn配置打包命令 {"name": "webpack-cs&q…

MIT6.824-Raft笔记3:Raft日志、应用层和raft之间的日志“传递“

1. 日志&#xff08;Raft Log&#xff09; 你们应该关心的一个问题是&#xff1a;为什么Raft系统这么关注Log&#xff0c;Log究竟起了什么作用&#xff1f; Log是Leader用来对操作排序的一种手段。这对于复制状态机&#xff08;复制状态机基于&#xff1a;对于复制的服务 ser…

Linux下基于MPI的hello程序设计

Linux下基于MPI的hello程序设计 一、MPICH并行计算库安装实验环境部署创建SSH信任连接&#xff0c;实现免密钥互相连接node1安装MPICH 3.4配置NFS注意(一定要先看)环境测试 二、HELLO WORLD并行程序设计 一、MPICH并行计算库安装 在Linux环境下安装MPICH执行环境&#xff0c;配…

Linux(CentOS7.5):通过docker安装redis

一、准备配置文件 在宿主机&#xff0c;准备映射配置文件的目录下&#xff0c;运行如下&#xff1a; wget http://download.redis.io/redis-stable/redis.conf二、安装 docker run \ --restartalways \ --log-opt max-size100m \ --log-opt max-file2 \ -p 6380:6379 \ -v /opt…

[ 持续更新 ] Sprint Boot 常用注解汇总

Sprint Boot 常用注解 请求与响应 RequestMapping 可以给类或类的属性设置该注解&#xff0c;表示支持的所有 HTTP 请求方法&#xff0c;如 GET、POST、PATCH、DELETE 等 如果给类设置&#xff0c;表示该路径的前缀。给方法设置&#xff0c;表示让这个方法支持所有的请求方…

【浅尝C++】C++类的6大默认成员函数——构造、析构及拷贝构造函数

&#x1f388;归属专栏&#xff1a;浅尝C &#x1f697;个人主页&#xff1a;Jammingpro &#x1f41f;记录一句&#xff1a;好想摆烂&#xff0c;又好想学习~~ 文章前言&#xff1a;本篇文章简要介绍C类的构造函数、析构函数及拷贝构造函数&#xff0c;介绍每个小点时&#xf…