【夜深人静写数据结构与算法 | 第八篇】哈希算法与哈希表

目录

前言:

哈希: 

哈希表:

哈希表组成:

哈希表实例:

哈希函数:

 TIPS:

总结


前言:

        如果此时我要你默写一个有一百位的数字,你要如何做才能保证不会漏写呢?我们有一种方法很好用:直接数我们写的数字有没有100个就好了,而这种把长的信息串压缩起来的方法就叫做哈希。

哈希: 

        哈希算法是一种将任意长度的信息压缩到一个固定长度的摘要信息的算法。哈希算法将原始数据映射成一段长度固定、内容任意的二进制字符串,该字符串通常称为哈希值、消息摘要或数字指纹。

注意点:有人看到了哈希可以转换信息的优点,便认为哈希就是加密其实这种思想是错误的

因为加密是可逆的,而哈希是不可逆的,我们可以通过加密过的文件解密得到本体,但是无法通过哈希值得到本体。 

举例:

        网站账户的密码存储就是一个典型的哈希过程。当我们创建一个网站账户时,通常会设置一个密码。为确保安全性,网站通常会先使用哈希算法将密码字符串转换成一个哈希值,并将这个哈希值存储在服务器上。这样,即使黑客攻击了网站,也无法得到我们的明文密码。我们输入密码登录时,网站会再次对我们输入的密码进行哈希,然后将结果与存储的哈希值进行比较。如果结果匹配,则认为密码正确,允许我们登录。

举个简单的例子,假设我们在某个网站注册时,设置的密码为 “password123”,网站使用SHA256算法将其哈希成 “d3c59d25033dbf980d29554025c23a75” 并存储在服务器上。当我们登录时,我们输入密码 “password123”,网站再次使用SHA256算法将其哈希成 “d3c59d25033dbf980d29554025c23a75”,并发现与之前存储的哈希值相同,于是认为我们输入的密码是正确的,允许我们登录。由于哈希算法的不可逆性,黑客即使获取了存储的哈希值,也无法反向推导得到我们的密码。

哈希表:

而我们基于哈希这种算法,设计了一种数据结构:哈希表(散列表)

        哈希表是一种基于哈希算法实现的数据结构,也称为散列表。哈希表通过将元素的关键字映射到表中一个位置来支持高效的元素查找、插入和删除操作。哈希表通常由数组和哈希函数两部分组成。数组用于存储元素,哈希函数则将元素的关键字映射到数组中一个位置。

哈希表的查找操作非常快速,因为不需要对所有元素进行遍历,只需要先通过哈希函数计算出元素在数组中的位置,然后直接访问该位置即可。哈希表支持的插入和删除操作也非常高效,通常只需要做一次哈希计算和一次数组访问。

        然而,哈希表的性能也受到哈希冲突(多个元素被映射到同一个位置)的影响。为解决哈希冲突,通常会使用一些常用的解决方法,比如开放地址法和链表法。

        哈希表在现实中有广泛的应用,比如编译器符号表、字典、缓存、数据库索引等。哈希表不仅具有高效的数据操作特性,还具有空间效率高、易于扩展、支持动态增删的优点。

哈希表组成:

哈希表由两部分组成:数组和哈希函数。

  • 1. 数组:哈希表通常由一个数组来实现,它用于存储元素。数组的大小取决于哈希表需要存储多少元素。
  • 2. 哈希函数:哈希函数是将元素关键字转换为数组下标的映射函数。哈希函数的设计非常关键,因为它的好坏直接影响哈希表的性能。哈希函数应该能够将关键字均匀地散布到数组中,并且产生最小的冲突。

除了数组和哈希函数,哈希表还可能包含链表(或其他数据结构),用于解决哈希冲突。基于链表的哈希表通常称为哈希链表,当多个元素被哈希到同一个数组下标时,它们可以被放置在同一个链表中。

需要注意的是,哈希表并不保证元素的顺序,因此在使用哈希表时,不能依赖元素的顺序,而是要根据元素的关键字来进行数据操作。

哈希表实例:

在这里我们用实图解决一下什么是用链表解决的哈希值:
假设我们需要存储各种动物下的蛋需要我们归类,我们一般会想直接用数组存储:

 但是这就有一个问题:每一次寻找蛋都需要我们遍历数组,因为我们并不知道哪一个下标存储的是哪一个蛋。

这个时候有的聪明的人就提出了:那我记忆一下不就好了,0是鸡蛋,1是乌龟蛋,2是鸵鸟蛋。。。。。我们下次找蛋的时候直接查询一下这个记录,不就不用遍历数组了,也就是我下次要找乌龟蛋,我就直接找乌龟蛋的记录:乌龟蛋的对应的下标是1,那我直接提取数组中下标是1的蛋不就好了。

而这种把一个文本转化成为另外一个文本,方便我们使用的过程,就是哈希

但是如果再下一个鸡蛋呢?它不就与第一个位置的鸡蛋冲突了?我们把这种冲突叫做哈希冲突(哈希冲突是指多个不同的元素被哈希函数映射到同一个数组下标的情况)

解决哈希冲突的方法主要有以下两种:

        1. 链表法:将哈希表中冲突的元素放在同一个桶(数组位置)下,并通过链表(或其他数据结构)进行连接。当查询元素时,首先使用哈希函数计算出元素对应的桶,然后遍历该桶中的链表,找到对应的元素。链表法可以解决冲突,但在链表过长时查找效率会降低。

        2. 开放地址法:在发生哈希冲突时,通过重新计算哈希函数并找到下一个可用的桶。不同的开放地址法有不同的决策方法来决定下一个可用的桶,比如线性探测、二次探测和双重哈希等。开放地址法相对于链表法来说,具有更好的内存缓存性能,但适用于装载因子比较小(大于0.7时,性能开始下降)的哈希表。

一些其他的解决哈希冲突的方法还包括:再哈希法、伪随机序列法、公共溢出区法等。这些方法各有优缺点,选择合适的解决哈希冲突的方法需要根据实际应用场景和数据规模进行权衡。

链表法:
 

但是用链表来解决哈希冲突也有一定的危险性:可能会出现数据都集中在几个链上的情况,那么哈希表快速查找的功能岂不是基本丧失了?因此在实际开发中我们不会使用这种方法进行存储。

 开放地址法:
如果此时在下一个蛋,这个蛋的位置上已经有旧蛋了,那么我们就再找一个空间就好了。

不过这种方法还是会让相同的数据聚集在一起,因此我们的开放地址法这个寻址的过程,我们又设计了二次探测方法。 

以上只是一个演示,让我们更好的理解哈希函数,但实际生活中我们通常不会以汉字这种形式来做哈希函数的结果。

哈希函数:

哈希函数是哈希表存储和查找的核心,它的好坏决定了哈希表的性能。一个好的哈希函数应该具有以下特点:

1. 高效:哈希函数的计算速度应该非常快,否则就会降低哈希表的性能。
2. 均匀:哈希函数应该将元素的关键字均匀地散布到哈希表中不同的数组下标中,以避免哈希冲突。
3. 独特:哈希函数计算出来的哈希值应该尽可能的独特,以防止不同元素在哈希表中映射到同一个数组下标,并且哈希冲突的概率要尽可能小。

如果哈希函数设计得好,就可以在常数时间内计算出对应元素的哈希值,并将元素存储到相应的位置中。而如果哈希函数不好,就可能导致冲突严重、存储效率低下,或者查询、插入或删除元素的性能变得非常低。

在实际应用中,哈希函数也需要考虑到哈希表的负载因子、元素的数据类型和数据分布等因素。一些常见的哈希函数包括:简单取模法、乘法哈希法、SHA哈希法等。

        1. 简单取模法:这是一种最简单的哈希函数,它的计算公式是将元素关键字除以哈希表的大小,然后将余数作为该元素的哈希值。简单取模法的优点是计算速度快,但如果哈希表的大小和元素的数目不匹配,就容易产生哈希冲突。为了减少冲突,在选择哈希表的大小时应该选择一个质数。

        2. 乘法哈希法:这种哈希函数计算元素哈希值的公式是:h(k) = floor(m * (kA mod 1)),其中k是元素关键字,A是一个介于0和1之间的常数,m是哈希表的大小,"floor"表示向下取整。这种方法相对于简单取模法来说,生成的哈希值更加分散,冲突率更低。

        3. SHA哈希法:SHA(Secure Hash Algorithm,安全哈希算法)是一种加密哈希函数,通常用于对数据进行不可逆的加密处理。SHA算法产生的哈希值通常是固定长度的,同时它生成的哈希值是基于输入数据唯一的,即使输入数据稍有变化,也会产生截然不同的哈希值。基于SHA哈希函数的哈希表在安全性和随机性方面都具有很好的性质,但计算相对较慢,不适用于哈希表中元素较多的情况。

 TIPS:

        虽然我们把一个数字经过哈希加密之后无法逆向得到原来的数字,但是我们还是可以通过撞库的方式来得到这个数字(这里我们采用的是SHA256加密):

        撞库这种手法很简单,就是暴力测试,就好比你想蹭到邻居家里的WIFI,那么你就有两种方式:1.直接去要密码。2.暴力测试,尝试所有可能的密码,直到密码正确为止。

而这种暴力测试所有密码,就是撞库。

加密后: 

不断进行撞库: 

 

也就是说虽然我们没有办法通过已经加密后的字符串来解密出数字1234,但是我们可以不断的实验所有的数字,肯定能够找到一个加密串跟这个串相等,那么我们就得到了密码。

总结

哈希算法是一个归类的算法,而由它组建起来的哈希表更是一个极其重要的数据结构,在很多高级语言中都有实现,因此我们要掌握好他的底层原理,才可以更好的运用这种数据结构形式。 

如果我的内容对你有帮助,请点赞,评论,收藏。创作不易,大家的支持就是我坚持下去的动力!

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

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

相关文章

算法-双指针-秋招算法冲刺

秋招冲刺算法 双指针 数组划分,数组分块 常⻅的双指针有两种形式,⼀种是对撞指针,⼀种是左右指针。 快慢指针 基本思想:使用两个移动速度不同的指针在数组或链表等序列结构上移动。通常处理结构类型:环形链表或数组…

Cortext-M3系统:储存器系统(2)

1、存储系统功能概览 Cortext-M3储存器有如下特点: 存储器映射是预定义的,并且还规定好了哪个位置使用哪条总线。 存储器系统支持所谓的“位带”(bit-band)操作。通过它,实现了对单一比特的原子操作,位带操…

【数据库三】数据库的存储引擎

存储引擎 1.存储引擎1.1 概念介绍1.2 常用存储引擎 2.MyISAM2.1 特点介绍2.2 支持的存储格式2.3 适用的生产场景 3.InnoDB3.1 特点介绍3.2 适用生产场景分析4.企业选择存储引擎依据 5.MyISAM和InnoDB的区别命令操作 1.存储引擎 1.1 概念介绍 MySQL数据库中的组件,负…

腾讯云+PicGo+Typora图床,生成专属图片链接

腾讯云PicGoTypora搭建自己的图床 原创声明,转载请注明文章链接来源、作者信息 TyporaPicGogitHub搭建自己的图床,写作效率大大提升 索奇问答 问:图床是什么? 答:用户可以将图片上传到图床,然后将生成的…

基于U-Net网络实现图像分割

目录 1、作者介绍2、U-Net网络及数据集介绍2.1 U-Net网络2.2 数据集介绍2.2.1 VOC_2012数据集2.2.2 眼球毛细血管数据集2.2.3 医学图像数据集 3、U-Net实现图像分割3.1 U-Net实现图像分割实验(简易版本)3.1.1 环境配置3.1.2 数据集准备3.1.3 代码实现3.1…

(十)异步-使用异步(3)

一、GUI 程序中的异步操作 1、在 GUI 程序中使用异步操作 在 GUI程序中, 首先理解关于 UI 显示变化的概念。 消息: UI 上的行为,如点击按钮、展示标签、移动窗体等。消息队列: 把要触发的所有消息,都按照相关的顺序…

CSDN 周赛 59 期

CSDN 周赛 59 期 前言判断题单选题题目1题目2填空题编程题1、题目名称:坏掉的打字机2、题目名称:布尔零点计数小结前言 由于最近,csdn 每日一练新增了两个题目,按照惯例,那么新增的题目,会就近出现在最近的 CSDN 周赛中,嗯,经常参加周赛,并关注每日一练社区的小伙伴应…

IO流(C++)

IO流C C语言的输入与输出流是什么CIO流C标准IO流C文件IO流二进制读写文本读写 stringstream的简单介绍 C语言的输入与输出 C语言中我们用到的最频繁的输入输出方式就是scanf ()与printf()。 scanf(): 从标准输入设备(键 盘)读取数据,并将值存放在变量中。printf():…

阿里云国际站代理商:如何优化阿里云服务器的性能和响应速度?有哪些调优策略和建议?

随着互联网的发展,阿里云服务器已经成为很多企业和个人的首选解决方案。然而,面对不断增长的需求和复杂的网络环境,如何优化阿里云服务器的性能和响应速度,提高用户体验,是很多用户关心的问题。本文将从以下几个方面&a…

MsSqlServer配置管理器TCP/IP属性

TCP/IP 属性(“IP 地址”选项卡) 使用 “TCP/IP 属性(‘IP 地址’选项卡)” 对话框,可以配置特定 IP 地址的 TCP/IP 协议选项。 只有选中 “IP All” ,才能一次配置所有地址的 “TCP 动态端口” 和 “TCP…

Debian openssh-server 的安装

在之前安装系统的时候有一个安装 SSH 服务的,结果没点上,导致系统完成后,ssh无法连接上啊,于是要安装sshd 服务。使用命令:apt-get install openssh-server 结果就出现问题了: 网上搜索说是要更新源&#x…

catkin cmake官方教程解读以及资料补充

这里写目录标题 报错cmakei下载cmake 官方教程教程1step1最低版本 报错报错2 vscode 路径没有配置好setting.json通过该方式打开的似乎是一个全局的文件,可以为本工作文件夹下设置一个本地的吗 报错3配置cmake工具链准确的流程报错4 cpp中main函数返回值问题结果 官…

Verilog基础:标识符的向上向下层次名引用

相关文章 Verilog基础:表达式位宽的确定(位宽拓展) Verilog基础:表达式符号的确定 Verilog基础:数据类型 Verilog基础:位宽拓展和有符号数运算的联系 Verilog基础:case、casex、ca…

如何在Microsoft Excel中使用TRUNC函数

Excel 中有多种删除小数点和缩短数值的方法。在本文中,我们将解释如何使用 TRUNC 函数,以及它与其他技术的不同之处。 TRUNC函数 什么是 TRUNC 功能如何使用 TRUNC 函数从日期时间戳中删除时间什么是 TRUNC 功能 TRUNC 函数将数字截断为指定的小数位数。使 TRUNC 不同于其他…

概率论与数理统计教程第五章节笔记

参考书籍:概率论与数理统计教程第三版 茆诗松 程依明 濮晓龙 编著 文章声明:如有错误还望批评指正 文章目录 ξ 5.1 \xi5.1 ξ5.1总体与样本 ξ 5.2 \xi5.2 ξ5.2样本数据的整理与显示Python绘制直方图Python绘制茎叶图 ξ 5.3 \xi5.3 ξ5.3统计量及其分…

基於Hadoop HA 在kerberos中配置datax

概要 提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 概要 前言一、基於HADOOP HA 搭建datax二、基於HADOOP HA 配置好的datax去配置kerberos1.在datax的配置文件中進行配置2.在shell腳本中加入認證語句 总结 前言…

layui框架学习(27:弹出层模块_其它用法)

除了前几篇文章介绍的弹出框类型外,layui的layer弹出层模块还支持相册框和tab框,所谓相册框即点击图片或按钮后会出现一个类似相册的页面单独浏览、切换图片,而tab框是指弹出框的显示形式类似于Winform中的TabControl控件,能以选项…

【Spring Security】的RememberMe功能流程与源码详解

文章目录 前言原理 基础版搭建初始化sql依赖引入配置类验证 源码分析 进阶版集成源码分析疑问1疑问2 鉴权 升级版集成初始化sql配置类验证 源码分析鉴权流程 扩展版 前言 之前我已经写过好几篇权限认证相关的文章了,有想复习的同学可以查看【身份权限认证合集】。今…

MIT 6.S081 Lab Four

MIT 6.S081 Lab Four 引言trapsRISC-V assembly (easy)代码解析 Backtrace(moderate)代码解析 Alarm(Hard)test0: invoke handler(调用处理程序)test1/test2(): resume interrupted code(恢复被中断的代码)代码解析issue解答 可选的挑战练习 引言 本文为 MIT 6.S081 2020 操作…

JDBC BasicDAO详解(通俗易懂)

目录 一、前言 二、BasicDAO的引入 1.为什么需要BasicDAO? 2.BasicDAO示意图 : 三、BasicDAO的分析 1.基本说明 : 2.简单设计 : 四、BasicDAO的实现 0.准备工作 : 1.工具类 : 2.JavaBean类 : 3.BasicDAO类 / StusDAO类 : 4.测试类 : 一、前言 第七节内容…