STL源码剖析笔记——哈希表、unordered_set、unordered_map、unordered_mutiset、unordered_mutimap

系列文章目录

STL源码剖析笔记——迭代器
STL源码剖析笔记——vector
STL源码剖析笔记——list
STL源码剖析笔记——deque、stack,queue
STL源码剖析笔记——Binary Heap、priority_queue
STL源码剖析笔记——AVL-tree、RB-tree、set、map、mutiset、mutimap
STL源码剖析笔记——哈希表、unordered_set、unordered_map、unordered_mutiset、unordered_mutimap

文章目录

  • 系列文章目录
    • 1. hashtable
      • 线性探测
      • 二次探测
      • 开链法
      • 哈希表扩容
        • 闭散列
        • 开散列
    • 2. unordered_set
    • 3. unordered_mutiset
    • 4. unordered_map
    • 5. unordered_mutimap


1. hashtable

  哈希表(hashtable)是一种常见的数据结构,它和红黑树、AVL一样是用来存储数据的。红黑树和AVL查找数据的时间复杂度是O(lgN) ,是对数平均时间。但哈希表的查找效率具有常数平均时间,复杂度是O(1)
  哈希表的底层是通过数组+hash function来实现常数时间查找效率。首先我们假设哈希表创建了一个大小为10的空间。
在这里插入图片描述
数据的存放通过hash function来决定,具体采用除留余数法
在这里插入图片描述
根据这个规则,可以将7,9,11对应存入数组;
在这里插入图片描述
但此时如果再想插入17,会发现位置已经被7给占了,这就是发生了哈希冲突。主要有三种方式来解决哈希冲突:
  1.线性探测
  2.二次探测
  3.开链法

线性探测和二次探测属于闭散列,开链法属于开散列。

线性探测

线性探测就是发现当前位置被占之后,顺序向后找,直到找到一个空的位置(如果到数组尾部仍未找到,则回到头部从头找);
在这里插入图片描述
在这里插入图片描述
但线性探测有一些无法避免的缺点:
聚集: 线性探测法容易导致聚集(clustering)现象。聚集指的是哈希表中形成连续的、紧密聚集的元素,这使得在插入元素时,发生冲突的概率进一步增加。聚集可能导致更多的线性探测,进而影响性能。

性能下降: 随着哈希表的填充因子增加,线性探测法的性能可能下降。填充因子是指已经存储的元素数量与哈希表大小的比率。填充因子增加会导致冲突的概率上升,进而增加线性探测的次数。

删除困难: 在使用线性探测法的散列表中删除元素可能比较困难。删除操作通常需要标记被删除的元素,否则会影响后续查找。而线性探测法对删除操作的支持相对较弱。

不适用于高负载因子: 当哈希表的负载因子较高时,即填充因子接近1,线性探测法的性能可能急剧下降。高负载因子会导致更频繁的冲突,线性探测的探测次数增多,使得查找、插入和删除的效率都降低。

二次探测

  二次探测的思路和线性探测一样,但这次查找的步长变为了指数:如果hash function计算出来的位置为H并且发生了冲突,就依次尝试H + 1²、H + 2²、H + 3²、H + 4²······
在这里插入图片描述
二次探测缓解了线性探测的聚集问题,但也有一定的缺点:

周期性聚集: 二次探测法可能导致周期性聚集问题。如果哈希表的大小是某个4k+3形式的素数,而二次探测的步长为2的幂(例如1, 4, 9, 16等),那么在一定条件下会形成周期性的聚集。这可能导致更多的冲突和性能下降。

容易形成死循环: 在某些情况下,特定的哈希表状态可能导致二次探测进入死循环。例如,如果哈希表中的某个位置发生冲突,而附近的位置也都被占用,那么二次探测可能永远无法找到空槽。

删除困难: 与线性探测法一样,二次探测法在删除操作上也可能比较困难。删除元素可能需要特殊的标记,以确保在查找时能够正确地处理已删除的槽。

不适用于高负载因子: 当哈希表的负载因子较高时,二次探测法的性能可能下降。高负载因子增加了冲突的概率,进而增加了二次探测的次数。

开链法

  STL中的哈希表采用开链法解决哈希冲突问题,开链法在每个单元维护一个单向链表,发生冲突的元素直接接到链表上。同时将数组替换为vector,方便空间扩充。hashtable只有前向迭代器,一个单元的list最后的元素指向下一个单元(即下一个list的头)。
在这里插入图片描述

哈希表扩容

闭散列

  我们定义一个负载因子来表示当前散列表的满溢程度:

                负载因子α = 元素个数 / 数组长度

  负载因子越高,当前散列表发生哈希冲突的概率越高,则整体效率越低。一般来说,负载因子超过0.5时要考虑增容。扩容后,由于数组长度发生变化,原数组中的所有元素要重新插入到新哈希表中。
在这里插入图片描述

开散列

  桶的个数是一定的,随着元素的不断插入,每个桶中元素的个数不断增多,极端情况下,可能会导致一个桶中链表节点非常多,会影响的哈希表的性能,因此在一定条件下需要对哈希表进行增容。开散列最好的情况是:每个哈希桶中刚好挂一个节点,再继续插入元素时,每一次都会发生哈希冲突,因此,在元素个数刚好等于桶的个数时,可以给哈希表增容。
  扩容后,由于桶的长度发生变化,原数组中的所有元素要重新插入到新哈希表中。

  对于整形数据可以直接进行取模运算,但是如果要存的数据是字符串的类型呢?我们要自己配套实现一个仿函数,如果是int就自己实现int类型的仿函数,如果是string就自己实现string类型的仿函数。然后用仿函数去计算。

2. unordered_set

  unordered_set的底层实现容器是hashtable(哈希表),可以进行高效的搜索、插入和删除操作,时间复杂度是O(1),unordered_set不允许两个元素有相同的键值。

  unordered_set与set相比,拥有更高的操作效率(set的操作效率是O(logn)),但存入unordered_set是无序的,不能保证输入进来数据的顺序!!!

  unordered_set的操作基本上都是对底层哈希表操作的引用:

begin()end():返回指向容器中第一个元素和最后一个元素之后的位置的迭代器。size():返回容器中元素的数量。empty():检查容器是否为空。如果为空,返回 true,否则返回 falsemax_size():返回容器可以容纳的最大元素数量。insert():将元素插入到容器中。如果元素已经存在,则不会插入。emplace():构造并插入元素。与 insert() 类似,但可以直接使用构造函数参数,避免额外的复制或移动操作。erase():从容器中删除指定的元素。clear():删除容器中的所有元素。find():查找容器中是否存在具有指定键的元素。如果找到,则返回指向该元素的迭代器;否则,返回指向容器结尾的迭代器。count():返回具有指定键的元素的数量。对于 unordered_set,结果只能是 01bucket_count():返回容器中的桶数量。load_factor():返回容器的负载因子。负载因子是元素数量与桶数量的比值。max_load_factor():返回或设置容器的最大负载因子。当实际负载因子超过最大负载因子时,容器会自动增加桶数量。rehash():设置容器的桶数量。当桶数量增加时,容器将重新分配其元素,以保持合适的负载因子。reserve():设置容器的最小桶数量,以便容纳指定数量的元素,而无需重新哈希。

3. unordered_mutiset

  unordered_multiset的特性以及用法和unordered_set完全相同,唯一的差别在于它允许键值重复(即插入重复的值)

4. unordered_map

  unordered_map的的底层实现容器是hashtable(哈希表),可以进行高效的搜索、插入和删除操作,时间复杂度是O(1),map的所有元素都是pair,同时拥有实值(value)和键值(key)。pair的第一元素被视为键值, 第二元素被视为实值。map不允许两个元素拥有相同的键值。

  unordered_map与map相比,拥有更高的操作效率(map的操作效率是O(logn)),但存入unordered_map是无序的,不能保证输入进来数据的顺序!!!

  unordered_map的操作基本上都是对底层哈希表操作的引用:

begin()	返回指向容器中第一个键值对的正向迭代器。
end() 	返回指向容器中最后一个键值对之后位置的正向迭代器。
cbegin()begin() 功能相同,只不过在其基础上增加了 const 属性,即该方法返回的迭代器不能用于修改容器内存储的键值对。
cend()end() 功能相同,只不过在其基础上,增加了 const 属性,即该方法返回的迭代器不能用于修改容器内存储的键值对。
empty()	若容器为空,则返回 true;否则 falsesize()	返回当前容器中存有键值对的个数。
max_size()	返回容器所能容纳键值对的最大个数,不同的操作系统,其返回值亦不相同。
at(key)	返回容器中存储的键 key 对应的值,如果 key 不存在,则会抛出 out_of_range 异常。 
find(key)	查找以 key 为键的键值对,如果找到,则返回一个指向该键值对的正向迭代器;反之,则返回一个指向容器中最后一个键值对之后位置的迭代器(如果 end() 方法返回的迭代器)。
count(key)	在容器中查找以 key 键的键值对的个数。
equal_range(key)	返回一个 pair 对象,其包含 2 个迭代器,用于表明当前容器中键为 key 的键值对所在的范围。
emplace()	向容器中添加新键值对,效率比 insert() 方法高。
emplace_hint()	向容器中添加新键值对,效率比 insert() 方法高。
insert() 	向容器中添加新键值对。
erase()	删除指定键值对。
clear() 	清空容器,即删除容器中存储的所有键值对。
swap()	交换 2 个 unordered_map 容器存储的键值对,前提是必须保证这 2 个容器的类型完全相等。
bucket_count()	返回当前容器底层存储键值对时,使用桶(一个线性链表代表一个桶)的数量。
max_bucket_count()	返回当前系统中,unordered_map 容器底层最多可以使用多少桶。
bucket_size(n)	返回第 n 个桶中存储键值对的数量。
bucket(key)	返回以 key 为键的键值对所在桶的编号。
load_factor()	返回 unordered_map 容器中当前的负载因子。负载因子,指的是的当前容器中存储键值对的数量(size())和使用桶数(bucket_count())的比值,即 load_factor() = size() / bucket_count()max_load_factor()	返回或者设置当前 unordered_map 容器的负载因子。
rehash(n)	将当前容器底层使用桶的数量设置为 n。
reserve()	将存储桶的数量(也就是 bucket_count() 方法的返回值)设置为至少容纳count个元(不超过最大负载因子)所需的数量,并重新整理容器。
hash_function()	返回当前容器使用的哈希函数对象。

5. unordered_mutimap

  unordered_multimap的特性以及用法和unordered_map完全相同,唯一的差别在于它允许键值重复(即插入重复的值)

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

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

相关文章

一套rk3588 rtsp服务器推流的 github 方案及记录 -01

我不生产代码,我只是代码的搬运工,相信我,看完这个文章你的图片一定能变成流媒体推出去。 诉求:使用opencv拉流,转成bgr数据,需要把处理后的数据(BGR)编码成264,然后推流…

字符串函数strtok

1.调用格式: 2.调用形式:char*strtok(char*p1,const char*p2),其中第二个是由分隔符组成的字符串,第一个为需要分隔的字符串 3.调用目的:将分隔符之间的字符串取出 4.调用时一般将源字符串拷贝后调用,因为此函数会将…

基于Unity3D 低多边形地形模型纹理贴图

在线工具推荐: 3D数字孪生场景编辑器 - GLTF/GLB材质纹理编辑器 - 3D模型在线转换 - Three.js AI自动纹理开发包 - YOLO 虚幻合成数据生成器 - 三维模型预览图生成器 - 3D模型语义搜索引擎 当谈到游戏角色的3D模型风格时,有几种不同的风格&#xf…

【工程实践】使用modelscope下载大模型文件

前言 Modelscope(魔搭社区)是阿里达摩院的一款开源模型平台,里面提供了很多的热门模型供使用体验,其中的模型文件可以通过git clone 快速下载。并且为模型提供了Notebook的快速开发体验,使用阿里云服务,不需…

【优选算法系列】【专题二滑动窗口】第三节.904. 水果成篮和438. 找到字符串中所有字母异位词

文章目录 前言一、水果成篮 1.1 题目描述 1.2 题目解析 1.2.1 算法原理 1.2.2 代码编写 1.2.3 题目总结二、找到字符串中所有字母异位词 2.1 题目描述 2.2 题目解析 2.2.1 算法原理 2.2.2 代码编写 …

SAP UI5 walkthrough step9 Component Configuration

在之前的章节中,我们已经介绍完了MVC的架构和实现,现在我们来讲一下,SAPUI5的结构 这一步,我们将所有的UI资产从index.html里面独立封装在一个组件里面 这样组件就变得独立,可复用了。这样,无所什么时候我…

队列的实现

学习就像一段长跑,比的不是谁跑得快,而是谁更能坚持!! 1 队列的概念及结构 队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出 FIFO(First In First O…

外网访问内网服务器使用教程

如何在任何地方都能访问自己家里的笔记本上的应用?如何让局域网的服务器可以被任何地方访问到?有很多类似的需求,我们可以统一用一个解决方案:内网穿透。内网穿透的工具及方式有很多,如Ngrok、Ssh、autossh、Natapp、F…

特殊进程之守护进程

文章目录 1、守护进程的概念2、如何查看守护进程3、编写守护进程的步骤3.1 创建子进程,父进程退出3.2 在子进程中创建新会话3.3 改变当前工作目录3.4 重设文件权限掩码3.5 关闭不需要的文件描述符3.6 某些特殊的守护进程打开/dev/null 4、守护进程代码示例 1、守护进…

[UNILM]论文实现:Unified Language Model Pre-training for Natural Language.........

文章目录 一、完整代码二、论文解读2.1 介绍2.2 架构2.3 输入端2.4 结果 三、过程实现四、整体总结 论文:Unified Language Model Pre-training for Natural Language Understanding and Generation 作者:Li Dong, Nan Yang, Wenhui Wang, Furu Wei, Xia…

MyBatis--07--启动过程分析、SqlSession安全问题、拦截器

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 谈谈MyBatis的启动过程具体的操作过程如下:实现测试类,并测试SqlSessionFactorySqlSession SqlSession有数据安全问题?在MyBatis中,SqlSess…

vuex如何存储数据、获取数据、以及数据的持久化

前提必须已经在vue中安装了vuex插件不然无法使用,不知道怎么创建vue和安装vuex的可以看这个视频,node.js版本最好16以上不然可能会安装失败:30分钟学会Vue之VueRouter&Vuex 趁着暑假掌握一门技能 大学生前端实习毕业设计必备技能_哔哩哔哩…

好代码资源网整站打包代码(包含了最新数据),集成了深度二开的ripro主题,非常适合做资源网站创业用

好代码资源网是基于wordpress开发的一个资源分享类网站,在开发者圈子里还算小有名气,这里分享婴整站打包代码(包含了最新数据)。网站本身集成了深度二开的ripro主题,非常适合做资源网站创业用。 资源下载类网站目前还…

Button背景颜色改不了,一直是默认的紫色

使用android.widget.Button <android.widget.Buttonandroid:layout_width"wrap_content"android:layout_height"wrap_content"android:onClick"doClick"android:text"这是一个按钮"android:textColor"color/black"androi…

kubesphere安装后启用DevOps

官方文档&#xff1a;KubeSphere DevOps 系统 1、集群管理---定制资源定义 进入目录&#xff1a;集群管理---定制资源定义搜索&#xff1a;clusterconfiguration 点击 ks-installer 右侧的 &#xff0c;选择编辑 YAML 在该 YAML 文件中&#xff0c;搜索 devops&#xff0c;…

No CUDA GPUs are available

文章目录 前言尝试方法一、尝试方法一二、尝试方法二 总结 前言 之前用服务器跑的时候&#xff0c;发现是可以跑的。但当有其他人一同使用的时候&#xff0c;就会抛出&#xff1a;No CUDA GPUs are available&#xff0c;这个时候我尝试了以下两种方式解决&#xff0c;后面终于…

一到冬天,助听器出现声音小、无声、时有时无……

冬天是一个寒冷干燥的季节&#xff0c;对于助听器的使用者来说&#xff0c;也是一个需要特别注意保养的季节。助听器是高精密的电子产品&#xff0c;如果不注意保养&#xff0c;可能会出现声音小、无声、时有时无等故障&#xff0c;影响听力康复的效果。那么&#xff0c;冬天我…

C++中string类的使用

目录 一.string类 1.1为什么学习string类&#xff1f; 1.2.标准库中的string类 二.string对象的元素访问 2.1.1使用operator[]与at实现访问 2.1.2正向迭代器访问 2.1.3反向迭代器访问 2.1.4const正向迭代器&#xff08;不能修改&#xff09; 2.1.5const反向迭代器&#…

垃圾收集算法和各种垃圾收集器的实现

深入理解Jvm虚拟机第三章 二、对象已死&#xff1f;3.2.1 引用计数算法3.2.2 可达性分析算法3.2.3 再谈引用3.2.4 生存还是死亡3.2.5 回收方法区 三、垃圾收集算法3.3.1 分代收集理论3.3.2 标记-清除算法3.3.3 标记-复制算法3.3.4 标记-整理算法 四、HotSpot的算法细节实现3.4.…

C# WPF上位机开发(串口界面设计)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 如果只是把上位机看成是纯软件开发&#xff0c;本身不和硬件打交道的话&#xff0c;那么这就把上位机的操作范围给限定死了。事实上&#xff0c;上…