【C++进阶】哈希表的闭散列和开散列(哈希桶)的代码实现

在这里插入图片描述

👦个人主页:@Weraphael
✍🏻作者简介:目前学习C++和算法
✈️专栏:C++航路
🐋 希望大家多多支持,咱一起进步!😁
如果文章对你有帮助的话
欢迎 评论💬 点赞👍🏻 收藏 📂 加关注✨


目录

  • 一、哈希表的闭散列实现
      • 1.1 结构
      • 1.2 插入操作
      • 1.3 查找操作
      • 1.3 删除操作
  • 二、哈希表的开散列实现(哈希桶)
      • 2.1 结构
      • 2.2 构造函数
      • 2.3 插入操作
      • 2.4 测试
      • 2.5 查找操作
      • 2.6 删除操作
      • 2.7 析构函数
  • 三、源码

一、哈希表的闭散列实现

1.1 结构

【分析】

  • 当在哈希表中删除值时,如何抹除数据?给个0或者-1,那如果数据本身就有0或者-1呢,因此不行;挪动数据覆盖就更加不行了,因为会影响数据的哈希值。
  • 当在哈希表中插入值时,如果不为空,说明该位置已存储数据,需要向后遍历,直到遇到空位置或者已经删除数据的位置插入

因此,在闭散列的哈希表中,哈希表每个位置除了存储所给数据之外,还应该存储该位置当前的状态,哈希表中每个位置的可能状态如下:

  1. EMPTY:当前是无数据的空位置
  2. EXIST:当前位置已存储数据
  3. DELETE:当前位置原本有数据,但被删除了

在这里插入图片描述

  • vector里面有一个size接口为什么还要再定义_n呢?

这是因为哈希表存储数据是分散存储的,因此size接口的大小是不确定的。

在这里插入图片描述

1.2 插入操作

  • 通过哈希函数计算出对应的哈希地址。当位置的状态是EMPTY或者DELETE就可以直接插入;如果是EXIT则要向后继续遍历(越界后需要从0开始)

在这里插入图片描述

然而如果哈希表中的元素接近满了,这时候就很容易引发哈希冲突,而哈希表不期望这样。那么扩容就很有必要,因此,哈希表引入了负载因子(载荷因子)

散列表的载荷因子定义为:α = 填入表中的元素个数 / 散列表的长度

  • α是散列表装满程度的标志因子。由于散列表是定值,α填入表中的元素个数_n成正比,所以,_n越大,表明填入表中的元素越多,产生冲突的概率就越大;反之,_n越小,标明填入表中的元素越少,产生冲突的概率就越小。
  • 注意:哈希表不能满了再扩容,需要控制负载因子一定值就扩容。
  • 对于散列表,荷载因子α是特别重要因素,应严格限制在0.7 - 0.8

在这里插入图片描述

以上这种扩容方式是由问题的

在这里插入图片描述

扩容完后,映射关系变了,需要重新映射

  • 现代写法

步骤如下:

  1. 创建新哈希表并扩容到足够的空间
  2. 遍历旧表的数据插入到新表即可(建立映射关系的工作交给Insert
  3. 交换新表旧表。

在这里插入图片描述

以上代码还有问题,假设key的类型是除了int之外的类型,那么除留余数法就走不通了!因为求余数的两个操作数必须是整数。

因此可以做两次映射。先让key和整型做映射( 使用仿函数),最后再和存储位置做映射。

在这里插入图片描述

有的人想:在仿函数那一块key不是被const修饰吗,为什么还是能修改key。比如const double key = 1.1转化成(size_t)key = 1

const只限制修饰的对象不能修改,但并不影响对其进行类型转换操作,这是合法的!

但是当测试string类型的时候,发现还是编译不通过

在这里插入图片描述

法一:可以再针对string类型写一个仿函数

在这里插入图片描述

然后编译就通过了

在这里插入图片描述

但是返回字符串的第一个字符的asc码也不好,万一序列中字符串第一个字符都相等,那么又会引发哈希冲突的概率就越大。因此,可以将字符串中的asc码全部相加再返回

在这里插入图片描述

但是因此方法还是完美避免哈希冲突。比如有这些字符串:"abc""bac"“cab”等九种排列组合,它们相加的asc码都是一样的。因此,就有人发明了字符串哈希算法:点击跳转。它的思想也是将所有字符的asc码相加,但是在相加之前乘以131

在这里插入图片描述

法二:类模板特化。好处:不用在main函数显示传递列表类型,由编译器自己匹配

在这里插入图片描述

同样也能编译通过

在这里插入图片描述

1.3 查找操作

  • 通过哈希函数计算出对应的哈希地址。

  • 如果位置的状态是EXIST则要比较key。相等返回,不相等继续向后找。

  • 如果位置的状态是DELETE还要继续向后找

  • 如果位置的状态是EMPTY就没有必要向后查找了

在这里插入图片描述

1.3 删除操作

  • 调用Find函数,如果找到了,直接在当前位置标记为DELETE;否则返回false

在这里插入图片描述

闭散列只做简单了解即可,真正重点在于开散列

二、哈希表的开散列实现(哈希桶)

2.1 结构

开散列又称为哈希桶。当发生哈希冲突时,直接在冲突位置挂着一个单链表,形似一个桶,也看作是一个指针数组

在这里插入图片描述

该结点类型除了存储所给数据之外,还需要存储一个结点指针用于指向下一个结点。

在这里插入图片描述

在开散列的哈希表中,哈希表的每个位置存储的实际上是某个单链表的头结点,即每个哈希桶中存储的数据实际上是一个结点类型

当然了,在插入数据时也需要根据负载因子判断是否需要增容,所以我们也应该存储哈希表中的有效元素个数,当负载因子过大时就应该进行哈希表的增容。
在这里插入图片描述

与闭散列的哈希表不同的是,开散列是将相同哈希值的元素都放到了同一个哈希桶中,并不需要经过探测寻找所谓的下一个位置,因此不需要存储状态字段

2.2 构造函数

  • 默认设置表长为10,并且初始化每个位置都为空nullptr

在这里插入图片描述

2.3 插入操作

在进行数据插入时,既可以尾插,也可以头插,因为桶中的存储顺序没有要求。为了操作简单,这里 选择头插

【头插思路】

在这里插入图片描述

【代码实现】

在这里插入图片描述

随着元素的不断插入,每个桶中元素的个数不断增多,极端情况下,可能会导致一个桶中链表节点非常多,。那么这就和链表没什么区别了,因此在一定条件下需要对哈希表进行增容

那该什么条件需要增容呢?

开散列的扩容还是要控制负载因子α = 填入表中的元素个数 / 散列表的长度对于开散列,一般控制在1,即填入表中的元素个数 == 散列表的长度

注意:扩容的时候不能像闭散列一样复用insert,因为开散列的insert是有new操作的,会有损耗

因此,这里可以步骤是:

  • 开一个新表
  • 遍历旧表的结点,通过计算结点在新表的哈希值,然后头插到新表中
  • 最后新旧表交换。

在这里插入图片描述

当然还要特殊处理key是不是整数的情况(和闭散列一样)

在这里插入图片描述

2.4 测试

在这里插入图片描述

【测试结果】

  • 插入十个数据(没有扩容)
    在这里插入图片描述

  • 插入十个数据(扩容2倍)

在这里插入图片描述

2.5 查找操作

步骤:

  • 首先计算查找元素的哈希值
  • 最后再根据哈希值去遍历链表

在这里插入图片描述

2.6 删除操作

步骤:

  • 通过哈希函数计算出key的哈希值。
  • 遍历链表查找并删除。要注意头删的情况

在这里插入图片描述

2.7 析构函数

因为有new操作,所以需要手动遍历将其释放,避免内存泄漏

在这里插入图片描述

三、源码

Gitee仓库链接:点击跳转

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

mariadb数据库——安装,创建数据库

MariaDB是一个流行的开源关系型数据库管理系统(RDBMS),它是MySQL的一个分支。 安装 apt -y install mariadb-servervi /etc/mysql/mariadb.conf.d/50-server.cnf character-set-server utf8mb4 collation-server utf8mb4_general_c…

什么时候要用到Reflect API?

参考文档 https://www.zhihu.com/question/460133198 https://cn.vuejs.org/guide/extras/reactivity-in-depth.html https://juejin.cn/post/7103764386220769311 Reflect API 一般搭配 Proxy API 一起使用。什么是 Proxy API 呢? 先回顾下 vue 的数据响应性是如何…

【已解决】卸载软件时显示“无法使用此产品的安装源,请确认安装源存在,并且你可以访问它”报错截图如下

卸载软件时显示“无法使用此产品的安装源,请确认安装源存在,并且你可以访问它”报错截图如下 使用Uninstall Tool软件强制删除,绕过软件自带的uninstall程序。(小白推荐,如下图) Uninstall Tool - Unique…

【DAY06 软考中级备考笔记】数据结构:树

数据结构:树 3月1日 – 天气:晴 之前在B站看的视频讲的是在太过简单,弃了。现在换了新的视频继续,后续会重新看前面的视频补过来。https://www.bilibili.com/video/BV1pT4m1S7uH/ 1. 树的基本概念 需要注意的是: 并不是…

代码随想录算法训练营第五天

● 自己看到题目的第一想法 242. 有效的字母异位词 方法&#xff1a; 方法一&#xff1a; 暴力法 1. 分别对s, t排序 2. 遍历s与t 判断s[i]!t[i] 返回 false 否则 返回true思路&#xff1a; 注意&#xff1a; 代码&#xff1a; bool cmp(char a, char b){return a<b;…

解决GitHub无法访问的问题:手动修改hosts文件与使用SwitchHosts工具

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua&#xff0c;在这里我会分享我的知识和经验。&#x…

Node.js+Express后端,自定义接口

6分钟学会Express 后端 API 开发 Node.js 2020最新版_哔哩哔哩_bilibili 要使用Node.js和Express搭建一个简单的后台服务器&#xff0c;用于接收带有token的请求头&#xff0c;你可以按照以下步骤进行操作&#xff1a; 首先&#xff0c;确保你已经安装了Node.js和npm&#xff0…

OpenAI员工自曝996作息表,网友:真正的卷不需要强迫

鱼羊 发自 凹非寺 量子位 | 公众号 QbitAI OpenAI也996&#xff0c;实锤了&#xff08;doge&#xff09;。 思维链作者、从谷歌跳槽OpenAI的Jason Wei刚刚分享了自己在OpenAI的一天&#xff1a; [9:00am] 起床 [9:30am] 搭乘Waymo前往Mission SF&#xff0c;途中在Tartine买…

一篇文章带你搞定企业级完整性能测试流程

大部分公司在最初试的阶段只会关心项目的基本功能&#xff0c;能用就可以。但是随着项目的成熟&#xff0c;用户量逐步的增大&#xff0c;线上经常就会出现一些系统崩溃&#xff0c;用户反映系统太慢等性能问题的爆发。所以&#xff0c;性能测试的需求就逐步变得迫切了。所以&a…

【笔记】深度学习入门:基于Python的理论与实现(六)

深度学习 深度学习是加深了层的深度神经网络 加深网络 本节我们将这些已经学过的技术汇总起来&#xff0c;创建一个深度网络&#xff0c;挑战 MNIST 数据集的手写数字识别 向更深的网络出发 基于33的小型滤波器的卷积层。激活函数是ReLU。全连接层的后面使用Dropout层。基…

varFormatter 数据格式化库 以性能优先的 快速的 内存对象格式转换

varFormatter 数据格式化 技术 开源技术栏 对象/变量格式化工具库&#xff0c;其支持将一个对象进行按照 JSON XML HTML 等格式进行转换&#xff0c;并获取到结果字符串&#xff01; 目录 文章目录 varFormatter 数据格式化 技术目录介绍获取方式 使用实例格式化组件的基本使…

【C++初阶】内存管理

目录 一.C语言中的动态内存管理方式 二.C中的内存管理方式 1.new/delete操作内置类型 2.new和delete操作自定义类型 3.浅识抛异常 &#xff08;内存申请失败&#xff09; 4.new和delete操作自定义类型 三.new和delete的实现原理 1.内置类型 2.自定义类型 一.C语…

电机应用-正点原子直流有刷电机例程笔记

目录 基础驱动实验&#xff1a;调速和换向 初始化工作 电机基础驱动API 电压、电流、温度检测实验 初始化工作 采集工作 编码器测速实验 编码器接口计数原理 初始化工作 编码器测速工作 速度环控制实现 PID相关函数 PID运算 电流环控制实现 PID相关函数 PID运算…

代码随想录算法训练营第三十三天|1005.K次取反后最大化的数组和、134. 加油站、135. 分发糖果

1005.K次取反后最大化的数组和 刷题https://leetcode.cn/problems/maximize-sum-of-array-after-k-negations/description/文章讲解https://programmercarl.com/1005.K%E6%AC%A1%E5%8F%96%E5%8F%8D%E5%90%8E%E6%9C%80%E5%A4%A7%E5%8C%96%E7%9A%84%E6%95%B0%E7%BB%84%E5%92%8C.…

iOS-设置指定边圆角(左上、左下等)

以UILabel举例&#xff0c;效果图如下&#xff1a; 代码如下&#xff1a; //设置左上与右下圆角&#xff08;可自行编辑指定圆角位置&#xff09; UIBezierPath *maskPath [UIBezierPath bezierPathWithRoundedRect:_sleepStateLabel.bounds byRoundingCorners:UIRectCornerT…

Python 全栈系列227 部署chatglm3-API接口

说明 上一篇介绍了基于算力租用的方式部署chatglm3, 见文章&#xff1b;本篇接着看如何使用API方式进行使用。 内容 1 官方接口 详情可见接口调用文档 调用有两种方式&#xff0c;SDK包和Http。一般来说&#xff0c;用SDK会省事一些。 以下是Python SDK包的git项目地址 安…

“环波罗的海”包围圈将正式形成

据“直新闻”的消息称&#xff0c;近日匈牙利国会同意了瑞典加入北约的申请&#xff0c;在走完相关后续程序后&#xff0c;瑞典就将成为北约第三十二个成员国&#xff0c;而北约对俄罗斯打造的“环波罗的海”包围圈也将正式形成&#xff0c;即除俄方外&#xff0c;波罗的海周边…

JEECG_ExcelExportServer批量数据导出超过60000条

项目上线了,结果导出数据时发现只能导出6w条,好奇怪啊... 本地试了试结果每次都卡在10w条. orz 开始扒拉批量导出 ExcelBatchExportServer server new ExcelBatchExportServer();server.init(exportParams,TTransLine.class);server.write(exportList);Workbook workbook s…

github如果给第三方项目提PR(Pull Request)

参考&#xff1a; https://blog.csdn.net/Leventcoco/article/details/135871779 1&#xff09;第一步 先fork第三方项目 点击fork然后就同步一份到自己名下了&#xff0c;后续修改在自己名下这项目上先修改&#xff1a; 2&#xff09;修改项目&#xff08;要提交的新功能或…

阿里云幻兽帕鲁服务器怎么续费?阿里云服务器租用价格优惠有哪些?

阿里云幻兽帕鲁服务器的续费可以通过登录阿里云账户&#xff0c;访问ECS控制台页面来进行。首先&#xff0c;需要在控制台中找到想要续费的幻兽帕鲁服务器实例。接着&#xff0c;在控制台页面左侧导航栏中找到“费用中心”&#xff0c;点击进入&#xff0c;在费用中心页面中找到…