【数据结构】你真的了解哈希表吗?看完你会对数据结构——哈希表, 会有更深更全面的认识 (理论篇)

本篇会加入个人的所谓鱼式疯言

❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言

而是理解过并总结出来通俗易懂的大白话,

小编会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的.

🤭🤭🤭可能说的不是那么严谨.但小编初心是能让更多人能接受我们这个概念 !!!

在这里插入图片描述

前言

在当今这个信息爆炸的时代,数据的快速检索和处理变得尤为重要。想象一下,如果你有一个巨大的图书馆,而你需要在几秒钟内找到特定的一本书,你会怎么做?这正是哈希表的魔力所在。哈希表以其高效的数据检索能力,成为了现代计算机科学中的一个关键技术。本文将深入探讨哈希表的工作原理、它如何优化数据存储和检索,以及它是如何解决数据存储中的冲突问题的。

目录

  1. 哈希表的初识

  2. 哈希冲突

一. 哈希表的初识

1. 哈希表的理解

什么是哈希表? 简单来说

哈希表就是一种把 key值 通过 哈希函数 映射到 数组中指定位置方便快速查找, 插入, 删除 的一种数据结构。

这里的 快速 , 可以达到多快呢? 大体上可以达到 O(1)时间复杂度常数级别 的效率。

其中这里的 Key 值, 不仅仅是 整数数据 , 也可以是 字符串包装类类型 的数据当做key 然后映射成数组的 索引(下标) , 用来方便查找。

试想一下, 如果有一排加了锁的柜子, 和手中有一串钥匙。

如果你的都 没有标记哪把钥匙对应几号柜子 , 就需要一个一个的进行比对, 这时就需要遍历 每一个把钥匙 来进行开锁, 这时的 效率就会很低

但是如果你标记了哪把钥匙对应着哪个柜子 , 比如这把黑色的钥匙上面标记着五号柜子, 然后我只要把黑色的钥匙对着五号柜子就可以很快的开锁, 不必遍历所有的钥匙来开五号柜子, 这样的 效率就会很高

在这里插入图片描述

对于 key 怎么利用 哈希函数 转化成 数组索引位置, 下面就让我们来看看吧 🥩🥩🥩

鱼式疯言

补充说明

哈希表又称之为 散列表 , 其中散列的英文就是 hash , 而key 的中文名称又称之为 关键码

哈希表的快就在于, 关键码不用进行多次比较来找到 自身的位置 , 就可以 快速的通过索引的位置 进行操作。

2. 哈希函数

对于 哈希函数 的设计有常用的两种方式

  1. 直接定制法

  2. 除留余数法

<1>. 直接定制法

直接定制法, 用公式法表示就是

Hash(k) = A* k + B 通过这样的方式, 将 key 的值转为通过这样的哈希函数转化为 一个值

这个哈希函数我们称之为 线性函数 , 而我们通过这个线性函数得到的这个值我们称之为 哈希值

小伙伴们可以先写下这道题:

题目: 字符串中出现一次的字符

题解代码:

class Solution {public int firstUniqChar(String s) {int []count=new int[26];for(int i=0;i<s.length();i++) {char ch=s.charAt(i);count[ch-'a']++;}for (int i = 0; i < s.length(); i++) {char ch=s.charAt(i);if(count[ch-'a']==1) {return i;}}return -1;}
}

在这道题中, 我们就利用这种哈希函数的方式和哈希映射的思维,

通过一个数组来模拟哈希表, 由于我们要寻找 出现字符的种类和个数, 所以我们就 new 一个长度为 26 的数组来模拟哈希表。

count[ch-'a']++;

因为都是 小写字母,我们要把该 小写字母 映射成 数组的下标 , 并且要从 0 开始映射, 我们就要在小写字母的基础上 减去一个 字符 ‘a’ , 从而可以映射 0~25 位置的 字符 , 并且这里数组的元素的值也可以表示次数

上面的栗子就可以体现字符通过 == Ak + B 的线性函数== 来映射下标值, 其中 A = 1 , B = - 26 , 从而确定好0~25 的下标范围。

充分体现了 哈希函数的在字符统计搜索 的作用。

鱼式疯言

补充说明

  1. 上述用数组来 模拟哈希表 的作用就在于:

下标 来表示 字符的种类 , 用 数组的值字符的出现的次数

  1. 线性哈希函数的缺点: 适合于数据 范围比较小连续 的情况
    优先: 简单,均匀

<2>. 除留余数法

对于 保留余数法 , 其实就很简单

先看公式吧:

hash(k) = key % capacity

其中 key 就是 关键码 , 而 ·capacity 就是 实际数组的容量大小 。 通过上述的 方案就讲key转化为哈希值。

在这里插入图片描述

如上图:

利用 key 通过 除留余数法 来获取 下标位置, 从而确定 查找的位置

鱼式疯言

以上的两种 哈希函数 获取 hashcode (哈希值) 的方法是比较常见的, 但是不是说是唯一两种方法的,在获取 哈希值的方案 是有很多的。


从上面的过程,虽然体现了 哈希函数 的简便好用, 但是使用哈希函数也会遇到相关的问题

二. 哈希冲突

在我们使用上面第二种方案: 除留余数法

这个过程中, 就有可能出现以下的情况

在这里插入图片描述

如上图中 , 56 通过哈希函数映射出的哈希值是 6, 而原先的 26 通过哈希值映射出来的哈希值也是 6

像上述, 两个不同关键码 通过 相同的哈希函数 映射出 相同哈希值 , 我们就称之为 哈希冲突 。 又称之为 哈希碰撞

居然出现了 哈希冲突 的这样问题, 那么我们就需要思路如何应对 哈希冲突 的方案

2. 负载因子

在谈及解决哈希冲突之前, 还有一个因素也会产生 哈希冲突 的情况 , 那就是 负载因子

首先, 我们要明确什么是负载因子 ?

负载因子 a = size / capacity , 其中 size 表示 现有数据个数capacity 表示 数组的容量大小

那么这个 负载因子和哈希冲突 之间是呈 什么关系 ? ? ?

在这里插入图片描述

从上面图像来看, 冲突率与负载因子呈 正相关 的关系, 也就是说 负载因子越大, 冲突率越高, 越容易产生 哈希冲突

小编是这么理解的

size代表是关键码的个数, 是做分子, 也就是说对于数组来说, 关键码插入的越多, 对于数组本身来说, 是不是剩余空间就越小,已插入的空间位置就越多, 那么就跟容易遇到已经被插入 关键码的位置空间


鱼式疯言

补充说明

从上图中, 我们可以看到 两个极端的点

负载因子为0 时, 冲突率为 0 , 就说明当 数组中没有元素 了 , 就一定不会产生哈希冲突。

负载因子为1 时, 冲突率为 1 , 就说明当 数组中的元素 .>= 数组的大小 时, 就一定会产生哈希冲突 , 所以一定要保证 数组容量 > 实际元素的大小


下面小编讲从以上两种可能产生哈希冲突的因素来 建立两种解决方案来 解决哈希冲突。

  • 从负载因子的角度来解决

3. 降低负载因子

由于 负载因子 a = size / capacity

公式的角度 来看, size 是确定了, 我们无法改变 关键码的个数, 所以我们只能改变 数组的容量

也就是说, 当负载因子达到一定的大小 , 就需要 对数组进行扩容 , 使capacity 增大, 从而是 a 减少, 进而降低 哈希冲突 的可能。

例如小编后面要讲解的 Java标准库 中实现的的 hashmap 的负载因子 是 0.75 ,当不断对 hashmap 中插入元素时, size 就会不断增大, 当 a >= 0.75 后, 就需要进行扩容操作。

  • 从哈希函数的角度来解决

4. 闭散列

<1>. 线性探测

如果发现下面这样的 哈希冲突

在这里插入图片描述

在这里插入图片描述

我们就可以向 下一个 没有存放 关键码的地方进行修改 , 如果下一个是存放过了关键码的哈希地址, 就会一直往后走, 直到遇到没有关键码的位置 才插入 当前关键码

鱼式疯言

补充说明

线性探测虽然简单,但是有一个不足的地方, 当出现了 哈希冲突 ,由于 一直向后 , 就会使 key 一直 集中在相邻的区域 ,会使 关键码比较集中

<2>. 二次探测

二次探测其实是一个重新利用一个哈希函数来寻找新的位置

H(i) =( h(0) + i ^ 2 ) / capacity

其中 H(i) 表示 新的哈希地址h(0) 表示发送哈希冲突的地址, i 代表 探测的次数

如果 第一次探测就 i 就放置为 1 , 如果探测的位置又遇到了 已经 存在关键码,那么 i++ , 就会进行 第二次探测 , 以此 循环往复, 最终执行找到 可以插入的空位置

5. 开散列

<1>. 哈希桶

在这里插入图片描述

如上图, 哈希桶的思维就是把数组上 每一个小集合 如上所示用 一个链表来连接

上图中在下标 6 位置已经存在26这个数据, 那么如果 56 要插入 到下标6位置 , 就需要进行尾插到 26节点的后面

这种方案的优点就在于如果发生 哈希冲突 , 多出来的关键码就可以连接到该 索引的链表的尾部 , 进行 尾插

这时肯定有小伙伴提出疑惑了, 这里需要要查找数据, 肯定要 遍历链表 吧, 那么遍历链表复杂度还是O(1) 吗?

答案: 还是

  • 因为在由于负载因子的存在,链表的长度不会太长, 所以 遍历起来的节点是很少

  • 并且对于哈希桶来说, 一旦 数组的长度 >= 64 并且 链表的长度 >= 8 的时候, 链表就会变成一颗 红黑树

所以时间复杂度也可近似等于 O(1)

总结

  • 哈希表的初识:除识了哈希表是一种讲关键码转化为 数组索引(下标)的一种能达到时间复杂度为 O(1) 的数据结构, 并通晓了常见的 两种哈希函数法 : 直接定制法和除留余数法

  • 哈希冲突: 两种可能产生哈希冲突的情况: 哈希函数的设计和 负载因子的增大 , 并列出两种解决哈希冲突的方案: 闭散列开散列

.

如果觉得小编写的还不错的咱可支持 三连 下 (定有回访哦) , 不妥当的咱请评论区 指正

希望我的文章能给各位宝子们带来哪怕一点点的收获就是 小编创作 的最大 动力 💖 💖 💖

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

实例讲解电动汽车VCU故障分类、故障码发送策略及Simulink建模方法

汽车作为一个上万零部件组成的工业品&#xff0c;从设计研发到试制调试再到路试可靠性测试再到车辆批量生产&#xff0c;要经历一个相当长的周期。在设计研发阶段&#xff0c;从设计方案与原理上尽量减少故障出现的可能&#xff0c;在试制调试阶段&#xff0c;通过全面的调试测…

车间设备巡检的意义与设备巡检系统的选择之道

在现代工业生产中&#xff0c;车间设备是企业的核心资产&#xff0c;其稳定运行直接关系到企业的生产效率、产品质量以及经济效益。而车间设备巡检作为设备管理的重要环节&#xff0c;具有不可忽视的重要性。 一、车间设备巡检的重要性 车间设备在长时间、高强度的运行过程中&…

C++番外篇——对于继承中子类与父类对象同时定义其析构顺序的探究

思考这样一串代码的运行结果&#xff1a; #include <iostream> using namespace std; class Person { public:~Person() { cout << "~Person()" << endl; } }; class Student:public Person { public:~Student() { cout << "~Student(…

Linus Torvalds解释为什么Linux开发人员趋向老龄化反而是件好事

Linux 的关键人物莱纳斯-托瓦尔兹&#xff08;Linus Torvalds&#xff09;说&#xff0c;尽管长期以来一直有关于开源软件开发领域出现倦怠的报道&#xff0c;但 Linux 仍一如既往地强大–尽管他承认&#xff0c;由于其规模和范围&#xff0c;他的项目也许是一个例外。 本周一&…

HTML引用CSS

CSS 样式需要引用到 HTML 中才能真正有效&#xff0c;那么如何才能在 HTML 中引用 CSS 呢&#xff1f;下面就来介绍一下。 1. 内嵌样式表 您可以在 HTML 头部&#xff08;<head>标签内&#xff09;的<style>标签中定义 CSS 样式&#xff0c;使用内嵌样式表定义的…

深入解读MaaS技术架构:从模型服务到智能部署的全流程分析

随着人工智能&#xff08;AI&#xff09;的迅速发展&#xff0c;MaaS&#xff08;Model as a Service&#xff0c;模型即服务&#xff09;技术架构应运而生。它通过将复杂的AI模型封装为标准化服务&#xff0c;降低了模型的开发和部署门槛&#xff0c;帮助企业快速实现业务场景…

传统产品经理如何快速转行成为顶尖的AI产品经理?

前言 产品经理本身便是一个需要不断学习、不断实践的岗位&#xff0c;即使是AI产品经理&#xff0c;也不能脱离产品经理岗位的本质。 另外&#xff0c;要想知道具体如何转行成为顶尖的AI产品经理&#xff0c;我们首先要明确两个问题&#xff0c;即&#xff1a; 什么是AI产品…

RAG 涨点小技巧——RAG上下文召回

昨天Claude团队发了一个关于RAG的博客&#xff0c;介绍了上下文召回的思路&#xff0c;可以看看。先看看标准的RAG&#xff08;检索增强生成&#xff09;是怎么做的&#xff1f; 将用于检索的知识库&#xff08;文档&#xff09;拆为小&#xff08;几百个token&#xff09;的文…

商业银行应用安全架构设计实践

传统的信息安全工作通常偏向于事中或事后检测漏洞,随着敏捷开发工作的逐步推进,商业银行认识到安全架构设计在实现IT降本增效方面的独特优势。近几年,商业银行逐步构建了安全架构设计工作体系,在组织人员、安全技术与管控流程方面,与企业IT架构密切协同,着力建设安全公共…

GPU与国产芯片异构通信方案,异构万卡集群 初步调研

视频分享在这&#xff1a; 3.1异构万卡集群&#xff0c;GPU与国产计算卡芯片异构通信_哔哩哔哩_bilibili 国内已经有三家&#xff0c;实现了异构集群&#xff0c;GPU与国产芯片异构通信方案&#xff0c;初步调用结果如下。 异构集群的挑战 异构芯片间的混训主要面临两大挑战…

《概率论与数理统计》学渣笔记

文章目录 1 随机事件和概率1.1 古典概型求概率随机分配问题简单随机抽样问题 1.2 几何概型求概率1.3 重要公式求概率 2 一维随机变量及其分布2.1 随机变量及其分布函数的定义离散型随机变量及其概率分布&#xff08;概率分布&#xff09;连续型随机变量及其概率分布&#xff08…

Java之线程篇六

目录 CAS CAS伪代码 CAS的应用 实现原子类 实现自旋锁 CAS的ABA问题 ABA问题导致BUG的例子 相关面试题 synchronized原理 synchronized特性 加锁过程 相关面试题 Callable 相关面试题 JUC的常见类 ReentrantLock ReentrantLock 和 synchronized 的区别: 原…

《大学操作系统课程:开启计算机世界的关键之门》

在大学的计算机科学与技术专业中&#xff0c;操作系统课程犹如一把钥匙&#xff0c;为学子们打开了深入了解计算机系统运行机制的大门。 操作系统课程首先会带领你探索操作系统的基本概念。你会明白操作系统是一种系统软件&#xff0c;它管理着计算机的硬件资源和软件资源&…

win系统接入google_auth实现动态密码,加强保护

开源代码地址&#xff1a;windows动态密码: 针对win服务器进行的动态密码管控&#xff0c;需要配合谷歌的身份认证APP使用 (gitee.com) 为什么要搞个动态密码呢&#xff1f; 首先云服务器启用了远程访问&#xff0c;虽然更换了端口以及初始用户名&#xff0c;不过还是是不是被…

核心复现—计及需求响应的区域综合能源系统双层优化调度策略

目录 一、主要内容&#xff1a; 二、摘要介绍&#xff1a; 三、综合能源系统结构&#xff1a; 四、实际仿真运行结果&#xff1a; 五、 代码及数据下载&#xff1a; 一、主要内容&#xff1a; 在模型构建部分&#xff1a;建立了一个综合能源系统双层优化调度模型&#xf…

南京服务器测评【浪浪云】

前言 优质的服务器对于企业来说无疑是一把快速实现科技化成长的利剑。而南京&#xff0c;作为中国科技龙头之一的城市&#xff0c;也对服务器的需求愈发旺盛。而作为国内领先的云服务商&#xff0c;浪浪云致力于用科技培植企业的成长&#xff0c;其在南京的服务器便是企业数字化…

计算机毕业设计springboot+vue项目分享在线服务平台

目录 功能和技术介绍系统实现截图开发核心技术介绍&#xff1a;使用说明开发步骤编译运行需求分析系统设计软件测试核心代码部分展示详细视频演示源码获取 功能和技术介绍 本项目包含程序源码和MySql脚本和文档,idea开发,支持Eclipse。使用vue的本质是SpringFramework【IoC&am…

0基础跟德姆(dom)一起学AI 数据处理和统计分析07-分组和会员数据分析

向量化函数及Lambda表达式 * 分组操作相关 * 分组聚合 * 分组转换 * 分组过滤 * DataFrameGroupBy对象介绍 * 会员分析案例-数据透视表 --- 1.向量化函数 * 分析代码 python def avg_test2(x,y): if x20: return np.NaN else: retu…

【OSS安全最佳实践】对OSS内身份证图片中身份证号进行脱敏

为确保存储在私有OSS Bucket特定文件夹中包含中国内地身份证信息的PNG、JPG、JPEG、BMP或WEBP格式图片&#xff0c;在与其他用户共享时身份证信息不被泄露&#xff0c;可使用数据安全中心 DSC&#xff08;Data Security Center&#xff09;的图片脱敏功能。DSC目前仅支持对身份…

react hooks--useRef

基本用法 在类组件中获取一个dom元素实例&#xff0c;可以通过React.CreateRef或者回调函数的方式去获取。语法&#xff1a;const refContainer useRef(initialValue);使用场景&#xff1a;在 React 中进行 DOM 操作时&#xff0c;用来获取 DOM作用&#xff1a;返回一个带有 …