哈希表、哈希冲突解决办法

文章目录

    • 一、什么是哈希表?
    • 二、什么是哈希冲突?怎样解决?
    • 三、哈希表的大小为什么是质数?
    • 四、链表法
    • 五、开放地址法
      • 线性探测法
      • 平方探测法
      • 双哈希(Double Hashing)
    • 六、哈希表满了怎么办?
    • 七、完美哈希
    • 八、一些使用哈希解决的算法题

在我以往的认知中,哈希就是进行唯一映射,指定一个关键字,就能映射唯一的值。平时经常使用linux下的 md5sum命令用来比较两个文件是否相同。至于如何哈希原理,它们是怎样进行唯一映射的,一直没有去梳理,如今该填的洞还是得补上。

一、什么是哈希表?

我们知道,普通数组能够直接寻址,在O(1)时间内能访问到数组中的任意位置。它需要足够大的空间,为每一个关键字保留一个位置。当关键字取值的范围很大,储存空间又有限时,能不能同样用数组的形式实现O(1)查找?

哈希表(Hash Table)就是这样的数据结构,当实际储存的关键字集合,比所有可能的关键字的全集小许多时,使用一个长度有限的数组去储存这些关键字,从而节省大量的空闲空间。它也被称作为散列表,因为它的键是分散存储在数组中的。

如图,黄色区域为键的全集,范围为0~99,绿色区域为实际存储的键。直接寻址需要为所有的键开辟空间,而哈希映射仅需为需要储存的键分配空间,储存空间比直接寻址少得多。

在这里插入图片描述

哈希表的核心思想是将键转换为数组的索引位置,以便快速查找对应的值。它使用哈希函数将键映射到数组的索引位置,这个索引位置称为哈希值。哈希映射是唯一映射,一个键永远只能产生一个哈希值,这种映射关系,就是我们所说的哈希函数。常见的哈希函数包括MD5、SHA-1、SHA-256等。

二、什么是哈希冲突?怎样解决?

通过取某运算,保持了哈希表中查找一个元素只要O(1)时间的优势。但细心的朋友会发现,上图中当键为7,17时,两个键会映射到同一个哈希值。我们称这种情形为哈希冲突,由于哈希函数不够“随机”,不同的键通过哈希函数计算得到相同的哈希值。

有人会说,简单,将哈希表增大,某上一个更大的值函数不就行了吗?可是可以,但哈希的精髓在于尽可能的节省空间与查找时间。最普通的哈希表,就是一个直接寻址的数组。不过,还有其它更高效的方法解决哈希冲突,如链表法,开放地址法等,这个后面再讲。

当哈希表中存储的键越来越多时,冲突的概率会不断增大,总会有哈希表再也装不下键的时候。所以,合理的控制一个哈希表的大小也极为重要。通常,将哈希表的大小设置为一个质数,是需要储存的键的数量的两倍。

三、哈希表的大小为什么是质数?

当哈希表的大小选择为一个合数(非质数)时,键的哈希值可能与哈希表的大小存在公因子。这种情况下,多个键的哈希值可能会映射到相同的索引位置,导致哈希冲突的概率增加。

让我们通过一个例子来说明这个问题。假设我们有一个哈希表大小为10,键的哈希函数将键映射到0到9的索引位置。我们有两个键:15和25。它们的哈希值分别为5和5。由于它们的哈希值都映射到相同的索引位置5,发生了哈希冲突。

倘若我们选择一个质数作为哈希表的大小,比如13,再次计算这两个键的哈希值。键15的哈希值为2,键25的哈希值为12。由于13是质数,与13存在公因子的数字较少,因此键的哈希值在13以内相同的概率会少很多。

通过选择质数作为哈希表的大小,可以减少键的哈希值与哈希表大小之间存在公因子的情况,从而减少哈希冲突的概率。这样可以更均匀地分布键值对,提高哈希表的性能和效率。

四、链表法

链表法其实很简单,就是将映射到同一地址的元素都放在一个链表中。哈希表中每个槽都有一个指针,指向所有映射到同一位置的元素链表表头,如果当前槽位没有这样的元素,头指针为空。

在这里插入图片描述

采用双向链表结构是为了删除与插入操作都能控制时间在O(1)范围内。每一个槽位链表的长度就是它的负载因子。通过上述结构不难看出,查找需要遍历链表,查找的时间取决于链表的长度。并且,当散列不均匀时,某一些链过长,有一些链很短,查找的效率也是不稳定的。

所以,一个好的哈希函数不应该将太多的输入映射到相同的输出。

五、开放地址法

除了链表法解决哈希冲突外,还可以通过开放地址的方式来解决哈希冲突。之所以叫开放地址,是因为它们都是通过将冲突的键,存放到哈希表空闲的其它地址中去,将未使用的地址开放出来,直到表空间耗尽。开方地址有多种方法,下面一一介绍。

线性探测法

探测(Probe),是指当地址冲突后,会继续去寻找表中可用的地址。线性探测,则是顺着当前位置向后查找。

如图,将关键字以线性探测的方式插入到哈希表中,哈希表的长度为13。

在这里插入图片描述

不难看出,线性探测随着填充因子的增加,冲突会聚集在一起,形成聚集效应。负载因子需要控制在70%左右,否则冲突次数飙升。数据分布本身不均匀也会导致冲突分布不均匀。线性探测法实现简单,但随着负载增加,性能下降明显。

平方探测法

如果遇到冲突,就往(原始位置 + i2)的位置寻找空位,i为查找次数。

如图:

在这里插入图片描述

平方探测法是解决哈希冲突的一种开放地址法。当发生哈希冲突时,它按一定的平方序列探测下一个位置。

优点:

  1. 降低了聚集效应。冲突分配更加均匀。
  2. 减少了聚簇内的查找时间。
  3. 可以提高负载因子。平均查找长度较短。

缺点:

  1. 需要计算平方序列值,实现较为复杂。
  2. 当Step超过表大小时计算会溢出。
  3. 随机性较差,数据分布不均会影响性能。
  4. 二次方探测发生冲突的概率较大。
  5. 删除操作会产生空洞,需要惰性删除避免影响。

总体而言,平方探测法通过牺牲部分实现复杂度和冲突计算时间来减轻聚集效应,在较高负载下性能较好。仍需要考虑非全域性问题。

什么是惰性删除?

惰性删除(Lazy deletion) 是一种常用的哈希表优化方法,主要应用于开放定址法中的删除操作。其基本思路是:

  1. 当删除键值对时,先将其标记为"已删除",而不直接从散列表中删除记录。
  2. 对后续插入操作,会扫描标记的已删除槽位并进行插入。
  3. 直到被后续插入覆盖,标记的删除状态才真正删除。

优点是可以避免删除后产生的空槽位,保证使用空间的连续性,减少冲突次数,提高效率。

缺点是需要扫描已删除槽位,增加查询的开销。

主要应用在平方探测法、线性探测法等开放定址中的删除操作,使得开放定址法的执行效率大为提升。是哈希表优化的重要手段之一。也可视作是一种延迟紧缩、空间换时间的实现。

双哈希(Double Hashing)

除了第一个哈希函数外,还有一个哈希函数用于解决冲突。

如果遇到冲突,新位置 = 原始位置 + i * hash2(key),i为查找次数。

hash2(key) = R - (key % R)
R要取比数组尺寸小的质数。
例如,哈希表长度为13,R = 7,hash2(key) = 7 - (key % 7)
也就是说,第二次哈希结果在1-7之间,不会等于0。

举例:
在这里插入图片描述

双哈希法的主要优点:

  1. 采用双重散列,冲突分布更加均匀。
  2. 相比线性探测,可以有效减少聚集问题。
  3. 指数增长级别更低,平均查找长度较短。
  4. 算法和实现都比较简单。
  5. 允许删除而不产生过多空槽。

双哈希法通过两次散列的思想,改进了性能和冲突分布,是一种效率较好的开方地址方法。

六、哈希表满了怎么办?

再哈希(rehash)

  • 当哈希表的插槽被占用了70%后,查找效率会严重下降,需要对哈希表进行扩展。

  • 新表长度为原来长度的2倍,且为质数。

  • 将旧表中的关键字,通过新的哈希函数,重新填充到新表中。

由此,我们也能看到哈希表有以下缺点:

  • 当表中的元素逐渐增多时,哈希冲突的概率会增加,查找效率会下降。

  • 哈希表扩展时,需要将旧表的关键字重新哈希,迁移到新表中,性能成本大。

  • 一个好的哈希表,依赖哈希函数的合理性。

七、完美哈希

完美哈希(Perfect Hashing),是一种解决哈希冲突的方法,它通过构建一个无冲突的哈希函数来实现。

完美哈希通过两级哈希的方式来解决哈希冲突。首先,使用一个一级哈希函数将键映射到一组桶(bucket)中。然后,在每个桶中,使用二级哈希函数将键映射到具体的索引位置。通过这种方式,每个键都被映射到唯一的索引位置,从而实现了完全散列。

完美哈希在某些特定场景下非常有用,特别是当键的集合是已知的且静态的情况下。它可以在预处理阶段构建完全散列函数,然后在查询时实现O(1)的查找时间复杂度,而无需处理冲突。然而,构建完美哈希函数的过程可能会比较复杂且耗时,特别是在键的集合较大的情况下。

需要注意的是,完美哈希并不适用于动态的键集合,因为当键的数量发生变化时,可能需要重新构建哈希函数。

八、一些使用哈希解决的算法题

  1. 两数之和:给定一个整数数组和一个目标值,找出数组中和为目标值的两个数的索引。可以使用哈希表来存储数组元素和其索引的映射关系,然后遍历数组,查找目标值与当前元素的差值是否存在于哈希表中。
    两数之和 https://blog.csdn.net/qq_41099859/article/details/112464038

  2. 两个数组的交集:给定两个整数数组,求它们的交集。可以使用哈希集合来存储一个数组中的元素,然后遍历另一个数组,判断元素是否存在于哈希集合中。
    两个数组的交集 https://blog.csdn.net/ShenHang_/article/details/107319514

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

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

相关文章

Java抽象类和接口(2)

🐵本篇文章继续对接口相关知识进行讲解 一、排序 1.1 给一个对象数组排序: class Student {public String name;public int age;public Student(String name, int age) {this.name name;this.age age;}public String toString() {return "name:…

爆肝整理! Python 网络爬虫 + 数据分析 + 机器学习教程来了

前段时间,有小伙伴多次在后台留言询问 Python 爬虫教程的问题。经过这两个多月以来的收集与整理,汇集了多个高校以及公开课视频教程,包括 python 爬虫的入门、进阶与实践,共 9G 左右。爬虫作为机器学习语料库构建的主要方式&#…

Python中的sys模块详解

1. 简介 sys模块是Python标准库中的一个内置模块,提供了与Python解释器和运行环境相关的功能。它包含了一些与系统操作和交互相关的函数和变量,可以用于获取命令行参数、控制程序的执行、管理模块和包、处理异常等。 2. 常用函数和变量 2.1 命令行参数…

虹科Pico汽车示波器 | 汽车免拆检修 | 2016款东风悦达起亚K5车发动机怠速抖动严重、加速无力

一、故障现象 一辆2016款东风悦达起亚K5车,搭载G4FJ发动机,累计行驶里程约为8.2万km。该车发动机怠速抖动严重、加速无力,同时发动机故障灯异常点亮,为此在其他维修厂更换了所有点火线圈和火花塞,故障依旧,…

自驾游汽车托运是交智商税吗?

自驾游汽车托运是交智商税吗? 亲爱的小伙伴们 你们有没有遇到过这样的困扰: 自驾游时,车辆的运输问题让你头疼不已? 是选择自己驾驶还是托运呢? 今天,我就来给大家种草一下汽车托运的好处, 让你的自驾游之旅更加轻松愉快! 1️.…

idea spring initializr创建项目报错

闲来无事就想搞个项目练练手,没想到直接给我卡在项目创建上了,一个个问题最终迎刃而解。 1.上来就给我报了个maven的错 未解析的插件: ‘org.apache.maven.plugins:maven-resources-plugin:3.3.1’ 不慌,应该是maven的路径有问题&#xff0c…

本地Nginx服务搭建结合内网穿透实现多个Windows Web站点公网访问

文章目录 1. 下载windows版Nginx2. 配置Nginx3. 测试局域网访问4. cpolar内网穿透5. 测试公网访问6. 配置固定二级子域名7. 测试访问公网固定二级子域名 1. 下载windows版Nginx 进入官方网站(http://nginx.org/en/download.html)下载windows版的nginx 下载好后解压进入nginx目…

Flask WTForms 表单插件的使用

在Web应用中,表单处理是一个基本而常见的任务。Python的WTForms库通过提供表单的结构、验证和渲染等功能,简化了表单的处理流程。与此同时,Flask的扩展Flask-WTF更进一步地整合了WTForms,为开发者提供了更便捷、灵活的表单处理方式…

Linux(9):正规表示法与文件格式化处理

简单的说,正规表示法就是处理字符串的方法,他是以行为单位来进行字符串的处理行为,正规表示法透过一些特殊符号的辅助,可以让使用者轻易的达到【搜寻/删除/取代】某特定字符串的处理程序。 正规表示法基本上是一种【表示法】&…

【运营思维】美团面试题:如何把梳子卖给寺庙和尚?

Hello 小米的小伙伴们~ 欢迎来到小米的微信公众号!今天小米要和大家分享一道美团运营面试题,题目可真是独特——“如何把梳子卖给寺庙和尚?”想必大家一定兴奋不已吧! 首先,让我们理清思路,挑战这个看似不…

[学习笔记]IK分词器的学习

IK分词器有几种模式 # 测试分词器 POST /_analyze {"text":"黑马程序员学习java太棒了","analyzer": "standard" }# 测试分词器 POST /_analyze {"text":"黑马程序员学习java太棒了","analyzer": &quo…

最新AI创作系统ChatGPT系统运营源码+DALL-E3文生图+支持OpenAI-GPT全模型+国内AI全模型

一、AI创作系统 SparkAi创作系统是基于ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统,支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美,可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI…

1评论收藏分享抖店不要再无脑铺货了!这个方法学会,7天流量就起飞~

这2023年都马上过完了,你还在上一堆链接到抖店吗?要知道这样无脑铺货是拿不到大流量的。 哪今天我给大家分享一个,比较适合新手操作,也能快速起流量出单的方法。 。首先你的店铺拿不到流量,一定要先查清楚你为什么拿…

海外Leads Generation产业:中国出海群体的行业大机会

Leads Generation(简称LeadsGen)指的是集中精力吸引和开发潜在客户的营销策略。通过引导式的营销策略,企业分发内容吸引潜在客户,引导客户留下电话/邮件/姓名等信息。基于这些信息,企业可建立潜在客户数据库&#xff0…

P8A002-CIA安全模型-配置Linux描述网络安全CIA模型之可用性案例

【预备知识】 可用性(Availability) 数据可用性是一种以使用者为中心的设计概念,易用性设计的重点在于让产品的设计能够符合使用者的习惯与需求。以互联网网站的设计为例,希望让使用者在浏览的过程中不会产生压力或感到挫折,并能让使用者在使用网站功能时,能用最少的努力…

数据结构与算法编程题27

计算二叉树深度 #define _CRT_SECURE_NO_WARNINGS#include <iostream> using namespace std;typedef char ElemType; #define ERROR 0 #define OK 1 #define Maxsize 100 #define STR_SIZE 1024typedef struct BiTNode {ElemType data;BiTNode* lchild, * rchild; }BiTNo…

基于单片机的智能鱼缸(论文+源码)

1.总体设计 在本次设计中&#xff0c;其系统整个框图如下图2.1所示。其主要的核心控制模块由单片机模块&#xff0c;LCD显示模块&#xff0c;喂食模块&#xff0c;蜂鸣器模块&#xff0c;按键模块&#xff0c;复位电路&#xff0c;抽水电路&#xff0c;加热电路&#xff0c;加…

麒麟V10服务器搭建FTP服务

概念 1.1介绍 FTP&#xff1a;File transfer protocol 文件传输协议 1.2原理 默认采用被动模式 被动模式FTP 为了解决服务器发起到客户的连接的问题&#xff0c;人们开发了一种不同的FTP连接方式。这就是所谓的被 动方式&#xff0c;或者叫做PASV&#xff0c;当客户端通…

C#开发的OpenRA游戏之属性SelectionDecorations(10)

C#开发的OpenRA游戏之属性SelectionDecorations(10) 前面分析了选择属性,继续分析前面的内容,不过这里不再是选择,而是选择相关的属性。 当用玩家选择地图上一个物品,或者士兵,或者坦克时,就会在周边画上一些指示标记,并且有一个状态条。 通过上图,可以看到建筑物周…

Eureka简单使用做微服务模块之间动态请求

创建一个eureka模块,引入eureka 为启动项加上EnableEurekaServer注解 配置信息 orderService和userService的操作是一样的 这里以orderService为例: 引入eureka客户端 加上 LoadBalanced注解 配置 orderService和userService都配置好了之后 启动 这样我们在http://localhos…