[数据结构] 哈希结构的哈希冲突解决哈希冲突

标题:[C++] 哈希结构的哈希冲突 && 解决哈希冲突

@水墨不写bug



目录

一、引言

        1.哈希

        2.哈希冲突

        3.哈希函数

 二、解决哈希冲突

1.闭散列

 I,线性探测

II,二次探测

2.开散列


正文开始:

一、引言

        哈希表是一种非常实用而且好用的关联式容器,如果你刷过不少题,一定会惊叹哈希竟然能解决如此多的实际问题。

        但是哈希表令人头疼的问题是哈希冲突的问题。在具体讲解之前,我们先铺垫引入几个概念:哈希,哈希函数,哈希冲突。

        1.哈希

         哈希结构最明显的特点是高效。在以往的顺序结构以及平衡树中,元素关键码与其存储位置之间没有对应的关系,因此在查找一个元素时必须要经过关键码的多次比较。顺序查找时间复杂度为O(N),平衡树中为树的高度,即O(log2 N),搜索的效率取决于搜索过程中元素的比较次数。

最优的搜索方法:不经过任何比较,一次直接从表中得到要搜索的元素。

        如果存在一种存储结构,通过某种函数(hashFunc)使元素的存储位置与它的关键码(key)之间能够建立一一映射的关系,那么在查找时通过该函数可以很快找到该元素。

        当我们向该结构中:

插入元素的时候:根据插入元素的关键码,根据这个关键码来通过某种映射关系来得到哈希表中对应的存储位置,然后将这个元素存入哈希表的对应位置。

搜索元素的时候:对元素的关键码进行同样的计算,把求得的函数值当做元素的存储位置,在哈希表中按照此位置进行查找,若关键码相等,则搜索成功。

        这种存储结构和方法统称为哈希

        哈希方法中使用的转换函数称为哈希(散列)函数,构造出来的结构称为哈希表(Hash Table)(或者称散列表).

        2.哈希冲突

        我们可以设计一个简单的哈希表:10个位置,哈希函数也是非常简单的除留取余(插入元素除以表的大小,就通过哈希函数得到了这个值应该在表中存储的位置):

        用该方法进行搜索可以一次找到存储对应值的位置,因此搜索的速度比较快。

但是,如果向上面这样的哈希表中插入14呢?

        我们发现14的位置被4占据了,这就是哈希冲突。

        即:不同关键字通过相同哈希哈数计算出相同的哈希地址,该种现象称为哈希冲突或哈希碰撞。

        把具有不同关键码而具有相同哈希地址的数据元素称为“同义词”.

        3.哈希函数

 引起哈希冲突的一个原因可能是:哈希函数设计不够合理。

哈希函数设计原则:

        1.哈希函数的定义域必须包括需要存储的全部关键码,同时如果散列表允许有m个地址时,其值域必须在0到m-1之间。

        2.哈希函数计算出来的地址能尽可能的均匀分布在整个空间中。

        3.哈希函数应该比较简单

         我们需要了解一下常见的哈希函数的设计方法:

1. 直接定址法

        取关键字的某个线性函数为散列地址:Hash(Key)= A*Key + B

        优点:简单、均匀、一般不会出现哈希冲突

        缺点:需要事先知道关键字的分布情况

        使用场景:适合查找比较小且连续的情况

2. 除留余数法.

        设散列表中允许的地址数为m,取一个不大于m,但最接近或者等于m的质数p作为除数,按照哈希函数:Hash(key) = key% p(p<=m),将关键码转换成哈希地址.

        优点:适用情况广泛

        缺点:会出现哈希冲突,需要解决哈希冲突的问题

 二、解决哈希冲突

        哈希冲突的解决方法常用的有两种:闭散列与开散列。

1.闭散列

         闭散列:也叫开放定址法,当发生哈希冲突时,如果哈希表未被装满,说明在哈希表中必然还有空位置,那么可以把key存放到冲突位置中的“下一个” 空位置中去。

        寻找下一个“空位置”也有多种方法,这里介绍常见的两种:线性探测,二次探测。

 I,线性探测

         在上面的例子中,我们想要插入14,本来14经过哈希函数计算得到的位置是4,但是4这个位置已经被占据了。

        线性探测就是:从发生冲突的位置开始,一个一个向后探测,直到寻找到下一个空位置为止。

        a.插入

        首先通过哈希函数获取待插入元素在哈希表中的位置。 如果该位置中没有元素则直接插入新元素;如果该位置中有元素发生哈希冲突,使用线性探测找到下一个空位置,插入新元素:

        b.删除

        采用闭散列处理哈希冲突时,不能随便物理删除哈希表中已有的元素,若直接删除元素会影响其他元素的搜索。

        比如:删除元素4,如果直接删除掉,14查找起来可能会受影响。因此线性探测采用标记的伪删除法来删除一个元素。

        我们可以通过一个标记状态的变量来表示哈希表内的数据的状态:存在,删除,空(EXIST,DELETE,EMPTY):

enum STATE
{EXIST,DELETE,EMPTY
}    

        在封装哈希表中每一个数据的类型时,在每个数据结构体内加入一个表示状态的变量即可。对于一个哈希表的位置,如果没有元素插入过,状态为EMPTY;

        如果存在元素,状态为EXIST;

        如果原来存在元素,但是之后删除了,状态为DELETE;

不同的状态对于将来查找(find)的处理会有影响。 

II,二次探测

         通过了解上面的线性探测,你自然也会发现线性探测的困难:

        产生冲突的数据堆积在一块,这与其一个一个向下找空位置有关系,找空位置的方式就是挨着往后逐个去找.

        二次探测的找下一个空位置的方法就大不相同了:二次探测向下找的方式是依次加上位置差的平方:

H_i = (H_0 + i^2 )% m 或者H_i = (H_0 - i^2 )% m

        其中:i = 1,2,3…, H_0是通过散列函数Hash(x)对元素的关键码 key 进行计算得到的位置,m是表的大小。

        对于上面的例子,如果使用二次探测,插入的过程:

插入 44 的过程:
    1.44 的初始哈希值是  14 % 10 = 4 ,但是位置 4 已经被占用了。
    2.触发二次探测,从  i = 1  开始。对于  i = 1 ,探测位置是:(4 + 1^2) % 10 = 5 但位置 5 也被占用了。
    3.继续探测, i = 2  时,探测位置是:(4 + 2^2) % 10 = 8

位置 8 是空的,所以 14 被插入到位置 8。

        对于闭散列而言,哈希表是需要扩容的,因为我们每次插入的时候都需要保证哈希表有空余的位置,所以我们需要一个判断哈希表内数据 装满程度的标志因子:载荷因子

        载荷因子记为a,a越大,表明填入表中的数据越多,产生哈希冲突的可能就越大。反之则相反。

        对于开放定址法,载荷因子需要严格控制在0.7-0.8以下。当载荷因子接近这个值时,就需要扩容。

2.开散列

         开散列法又叫链地址法(开链法),首先对关键码集合用散列函数计算散列地址,具有相同地址的关键码归于同一子集合,每一个子集合称为一个,各个桶中的元素通过一个单链表链接起来,各链表的头结点存储在哈希表中:

        

        当插入14时,对4这个位置的链表头插即可:

 

 以上是哈希结构解决哈希冲突的方法。


完~

未经作者同意禁止转载 

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

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

相关文章

JS基础学习笔记

1.引入方式 内部脚本 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title> <…

Nginx跨域运行案例:云台控制http请求,通过 http server 代理转发功能,实现跨域运行。(基于大华摄像头WEB无插件开发包)

文章目录 引言I 跨域运行案例开发资源测试/生产环境,Nginx代理转发,实现跨域运行本机开发运行II nginx的location指令Nginx配置中, 获取自定义请求header头Nginx 配置中,获取URL参数引言 背景:全景监控 需求:感知站点由于云台相关操作为 http 请求,http 请求受浏览器…

抢鲜体验 PolarDB PG 15 开源版

unsetunsetPolarDB 商业版unsetunset 8 月&#xff0c;PolarDB PostgreSQL 版兼容 PostgreSQL 15 版本&#xff08;商业版&#xff09;正式发布上线。 当前版本主要增强优化了以下方面&#xff1a; 改进排序功能&#xff1a;改进内存和磁盘排序算法。 增强SQL功能&#xff1a;支…

C++笔试强训12、13、14

文章目录 笔试强训12一、选择题1-5题6-10题 二、编程题题目一题目二 笔试强训13一、选择题1-5题6-10题 二、编程题题目一题目二 笔试强训14一、选择题1-5题6-10题 二、编程题题目一题目二 笔试强训12 一、选择题 1-5题 引用&#xff1a;是一个别名&#xff0c;与其被引用的实…

计算机网络(二) —— 网络编程套接字

目录 一&#xff0c;认识端口号 1.1 背景 1.2 端口号是什么 1.3 三个问题 二&#xff0c;认识Tcp协议和Udp协议 三&#xff0c;网络字节序 四&#xff0c;socket编程接口 4.1 socket常见API 4.2 sockaddr结构 一&#xff0c;认识端口号 1.1 背景 问题&#xff1a;在进…

vue2-elementUI-初始化启动项目-git

前置基础 资料下载-阿里云盘 vueaxioselement-uinpmvscode 初始化项目 1.创建vue2工程 1.1 vue create projectName1.2 选择 1.3 初始化 vue-cli 的核心步骤&#xff1a; Manually select features (*) Babel ( ) TypeScript ( ) Progressive Web App (PWA) Support …

【H2O2|全栈】关于HTML(4)HTML基础(三)

HTML相关知识 目录 HTML相关知识 前言 准备工作 标签的具体分类&#xff08;三&#xff09; 本文中的标签在什么位置中使用&#xff1f; 列表 ​编辑​编辑 有序列表 无序列表 自定义列表 表格 拓展案例 预告和回顾 后话 前言 本系列博客将分享HTML相关知识点…

【 html+css 绚丽Loading 】000044 两仪穿行轮

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享htmlcss 绚丽Loading&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495…

4-1.Android Camera 之 CameraInfo 编码模板(前后置摄像头理解、摄像头图像的自然方向理解)

一、Camera.CameraInfo Camera.CameraInfo 是用于获取设备上摄像头信息的一个类&#xff0c;它提供摄像头的各种详细信息&#xff0c;例如&#xff0c;摄像头的方向、是否支持闪光灯等&#xff0c;以下是它的常用属性 static int CAMERA_FACING_BACK&#xff1a;表示设备的后置…

云计算之数据库

目录 一、RDS产品介绍及排障思路 1.1 云RDS数据库及其特点 1.2 云RDS数据库-规格 1.3 云RDS数据库-存储 ​1.4 云RDS数据库-安全 ​1.5 云RDS数据库-整体架构 1.6 RDS常见问题排查 ​1.6.1 如何解决无法链接RDS实例的问题 1.6.2 RDS实例存储空间使用率高&#xff0c;怎…

机器学习引领未来:赋能精准高效的图像识别技术革新

图像识别技术近年来取得了显著进展,深刻地改变了各行各业。机器学习,特别是深度学习的突破,推动了这一领域的技术革新。本文将深入探讨机器学习如何赋能图像识别技术,从基础理论到前沿进展,再到实际应用与挑战展望,为您全面呈现这一领域的最新动态和未来趋势。 1. 引言 …

计算机网络与Internet应用

一、计算机网络 1.计算机网络的定义 网络定义&#xff1a;计算机网络是指将地理位置不同的具有独立功能的多台计算机及其外部设备&#xff0c;通过通信线路连接起来&#xff0c;在网络操作系统&#xff0c;网络管理软件及网络通信协议的管理和协调下&#xff0c;实现资源共享…

chrome 插件开发入门

1. 介绍 Chrome 插件可用于在谷歌浏览器上控制当前页面的一些操作&#xff0c;可自主控制网页&#xff0c;提升效率。 平常我们可在谷歌应用商店中下载谷歌插件来增强浏览器功能&#xff0c;作为开发者&#xff0c;我们也可以自己开发一个浏览器插件来配合我们的日常学习工作…

【leetcode详解】爬楼梯:DP入门典例(附DP通用思路 同类进阶练习)

实战总结&#xff1a; vector常用方法&#xff1a; 创建一个长为n的vector&#xff0c;并将所有元素初始化为某一定值x vector<int> vec(len, x) 代码执行过程中将所有元素更新为某一值x fill(vec.begin(), vec.end(), x) // 更多实战方法欢迎参考文章&#xff1a;…

HumanNeRF:Free-viewpoint Rendering of Moving People from Monocular Video 翻译

HumanNeRF&#xff1a;单目视频中运动人物的自由视点绘制 引言。我们介绍了一种自由视点渲染方法- HumanNeRF -它适用于一个给定的单眼视频ofa人类执行复杂的身体运动&#xff0c;例如&#xff0c;从YouTube的视频。我们的方法可以在任何帧暂停视频&#xff0c;并从任意新的摄…

堆排序Java

思路 这个代码还不错 https://blog.csdn.net/weixin_51609435/article/details/122982075 就是从下往上进行调整 1. 如何将数组映射成树 对于下面这颗树&#xff0c;原来的数组是&#xff1a; 好&#xff0c;如果调整的话&#xff0c;我们第一个应该调整的是最下边&#x…

html记账本改写:数据重新布局,更好用了,没有localStorage保存版本

<!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><title>htm记账本</title><style>table {user-select: none;/* width: 100%; */border-collapse: collapse;}table,th,td {border: 1px solid …

探索EasyCVR与AI技术深度融合:视频汇聚平台的新增长点

随着5G、AI、边缘计算、物联网&#xff08;IoT&#xff09;、云计算等技术的快速发展&#xff0c;万物互联已经从概念逐渐转变为现实&#xff0c;AIoT&#xff08;物联网人工智能&#xff09;的新时代正在加速到来。在这一背景下&#xff0c;视频技术作为信息传输和交互的重要手…

SpringMVC使用:类型转换数据格式化数据验证

01-类型转换器 先在pom.xml里面导入依赖&#xff0c;一个是mvc框架的依赖&#xff0c;一个是junit依赖 然后在web.xml里面导入以下配置&#xff08;配置的详细说明和用法我在前面文章中有写到&#xff09; 创建此测试类的方法用于测试springmvc是具备自动类型转换功能的 user属…

Linux驱动.之字符设备驱动框架,新内核框架,设备树(二)

第一篇比较长&#xff0c;第二篇&#xff0c;继续写&#xff0c;内容有重复 一、字符设备驱动框架 在用户空间中调用open&#xff0c;打开一个字符设备&#xff0c;执行流程如下&#xff1a;最终会执行chrdev中的ops对应的open函数。