哈希表(HashTable),哈希冲突的避免、解决
文章目录
- 什么是哈希表
- 哈希表概念
- 哈希冲突
- 哈希冲突概念
- 解决冲突
- 闭散列
- 闭散列平均查找次数的问题
- 开散列/哈希桶
- 冲突严重时的解决办法
- 避免冲突
- 哈希函数设计
- 常见的哈希函数
- 负载因子调节
什么是哈希表
先举一个很常见的例子:
我们有一个衣柜
这是一个杂乱无章的衣柜,里面放了四季的衣服,每当要去找一件合适的衣服去穿的时候,要翻箱倒柜麻烦半天,为了解决这个问题,我们买了四个柜子,规定它们分别存放春夏秋冬四季的衣服:
从此之后找衣服就方便了很多,什么季节去哪个衣柜找就能找到合适的衣服,这个例子背后就是哈希表的原理。
哈希表概念
在顺序结构以及平衡树中,元素关键码与其存储位置之间没有对应的关系,因此在查找一个元素时,必须要经过关键码的多次比较。顺序查找的时间复杂度为o(N),平衡树中为树的高度,即O(log2N),搜索的效率取决于搜索过程中元素的比较次数。
我们理想的搜索方法是可以不经过任何比较,一次直接从表中得到要搜索的元素。
如果构造一种存储结构,通过某种函数使得元素的存储位置与它的关键码之间能够建立一一对应的关系,那么在查找时通过该函数可以很快找到该元素。
当向该结构中:
- 插入元素
根据待插入元素的关键码,以此函数计算出该元素的存储位置并按此位置进行处存放 - 搜索元素
对元素的关键码进行同样的计算,把求得的函数值当做元素的存储位置,在结构中按此位置取元素比较,若关键码相等,则搜索成功
上述方法就是哈希(散列)方法,哈希方法中使用的转换函数称为哈希(散列)函数,构造出来的结构称为韩系表(Hash Table)或者称为散列表。
举个例子:
数据集合:{ 1, 7, 6, 5, 9 }
哈希函数设置为:hash(key) = key % capacity;capacity为存储元素底层空间总大小
用该方法进行搜索不必进行多次关键码的比较,因此搜索的速度比较快。
可此时出现了这样一个问题,继续向上面这个集合插入元素44,会出现什么问题?
很明显[4]号为已经有4了,此时遇到的这个问题叫做哈希冲突。
哈希冲突
哈希冲突概念
不同的关键字,通过相同的哈希函数计算出相同的哈希地址,这种现象称为哈希冲突或者哈希碰撞。
遇到冲突当然就是要解决冲突了。
解决冲突
解决哈希冲突的两种常见方法是:开散列和闭散列。
闭散列
闭散列:也叫开放定址法,当发生哈希冲突是,如果哈希表未被装满,说明在哈希表中必然还有空位置,那么可以吧Key存放到冲突位置中的“下一个”空位置中去,那么如何寻找下一个空位置呢?常见的有以下两种解决方法:
线性探测
比如上面的场景,现在需要插入元素44时,先通过哈希函数计算哈希地址,下标为4,因此44理论上应该插在该位置,但是该位置已经放了值为4的元素,即发生哈希冲突。线性探测法就是从发生冲突的位置开始,依次向后探测,直到找到下一个空位置为止,像上面例子44最后就被插到下标为8的位置:
二次探测
线性探测法的缺陷是产生冲突的数据堆积在一块,当要插入数字8时,它不能插入到本来通过计算得到的下标8处,要一直寻找空位到下标0,当数据堆积的多的时候无疑增加了操作的时间,二次探测法为了缓解这个问题,找下一个空位的方法是:第一次往后找找1²个,第二次找2²个,第三次找3²个。。。。。。
当然还有三次探测法,四次探测法等等。
闭散列平均查找次数的问题
例题:
关键字集合放入到哈希表中:
- 平均成功查找次数:
成功查找次数就是对于一个关键字要比较几次才能找到他,比如要找19,哈希地址为8,去下标8找19,第一次就找到了,1这就是19的成功查找次数,对于11,哈希地址为0,去下标0找没有,1找没有,2找没有直到5才找到,那么一共找了6次才找到,6就是11的成功查找次数, 那么平均成功查找次数 = 成功查找总次数(每个关键字的平均查找次数加起来) / 关键字个数。 - 平均失败查找次数:
从下标0开始找一直没找到就需要找10次到全数组找完还没找到,10就是下标0的失败查找次数,从下标1开始找需要查找9次。。。。。平均失败查找次数 = 所有下标失败查找次数和 / 数组长度。
开散列/哈希桶
开散列又叫链地址发,首先对关键码集合用哈希函数计算地址,具有相同地址的关键码归于同一子集合,每一个子集合称为一个桶,各个同种的元素通过一个单链表连接起来,各个链表的头结点存储在哈希表中:
从上图中可以看出,开散列中每个桶中放的都是发生哈希冲突的元素。
开散列,可以认为是把一个在大集合中的搜索问题转化为在小集合中搜索了。
冲突严重时的解决办法
刚才我们提到了,哈希桶其实可以看做将大集合的搜索问题转化为小集合的搜索问题了,那如果冲突严重,就意味着小集合的搜索性能也不佳时,这个时候我们就可以将小集合搜索问题继续转化:
- 每个桶的背后是另外一张哈希表、
- 每个桶的背后是一颗搜索树。
虽然说遇到哈希冲突我们可以用各种办法去尽量解决冲突,但是在冲突发生前我们能不能尽量避免冲突的发生呢?
答案是可以的:
避免冲突
首先,我们需要明确一点,由于我们哈希表底层数组(顺序结构)的容量往往是小于实际要存储的关键字的范围的,这就意味着冲突的发生是必然的,但我们能做的应该是尽量的降低冲突。有相面两个大的思路去降低冲突的发发生:
哈希函数设计
引起哈希冲突的一个原因可能是:哈希函数设计不够合理。
哈希函数设计原理:
- 哈希函数的定义域必须包括需要存储的全部关键码,而如果散列表允许有m个地址时,其值域必须在0到m - 1之间。
- 哈希函数计算出来的地址能均匀分布在整个空间中。
- 哈希函数应该比较简单
常见的哈希函数
- 直接定制法
取关键字的某个线性函数为散列地址: Hash( Key ) = A * Key + B
优点:简单、均匀
缺点:需要事先直到关键码的分布情况
使用场景:适合查找比较小且连续的情况 - 除留余数法
设散列表中允许的地址数为m,取一个不大于m,但最接近或者等于m的质数p作为除数,按照哈希函数:Hash(key) = Key % p (p <= m),将关键码转换成哈希地址。
哈希函数很多很多,这里就不做过渡到介绍了。
负载因子调节
哈希表的负载因子 = 填入表中的元素个数 / 散列表长度
哈希表的负载因子是哈希表装满程度的标志因子。由于表长是定值,负载因子和“填入表中的元素个数”成正比,所以负载因子越大,表明填入表中的元素越多,产生冲突的可能性就越大;反之,负载因子越小,表明填入表中的元素越少,长生冲突的可能性就越小。
对于开放定址法,负载因子是特别重要的因素,应严格限制在0.7 - 0.8以下。Java中将负载因子限制在0.75.超过此值将resize哈希表,即对底层顺序结构扩容然后将旧表中的数据重新一一放入到新表中。
这是负载因子和冲突率的粗略关系:
所以当冲突率达到一个无法忍受的程度时,我们需要通过降低负载因子来变相的降低冲突率。
哈希表(HashTable),哈希冲突的避免、解决相关推荐
- 高级数据结构与算法 | 哈希 :哈希冲突、负载因子、哈希函数、哈希表、哈希桶
文章目录 哈希 哈希函数 常见的哈希函数 字符串哈希函数 哈希冲突 闭散列的解决方法 开散列的解决方法 负载因子以及增容 对于闭散列 对于开散列结构 具体实现 哈希表(闭散列) 插入 查找 删除 完整 ...
- 哈希表及哈希冲突解决办法
哈希表及哈希冲突解决办法 目录 什么是哈希表? 哈希表的数据结构 哈希冲突 哈希冲突解决办法 1. 什么是哈希表? 哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直 ...
- 哈希表构造与处理冲突方法
我们知道:哈希表是一个固定大小的数组,数组的每个元素是一个链表(单向或双向)的头指针.如果Key一样,则在一起,如果Key不一样,则不在一起.哈希表的查询是飞快的.因为它不需要从头搜索,它利用Key的 ...
- 哈希表(哈希函数和处理哈希冲突)_20230528
哈希表(哈希函数和处理哈希冲突) 前言 关于哈希表的主题的小记原计划5月23日完成,由于本人新冠阳性,身体发烧乏力,周末感觉身体状况稍加恢复,赶紧打开电脑把本文完成,特别秉承"写是为了更好地 ...
- 哈希 :哈希冲突、负载因子、哈希函数、哈希表、哈希桶
文章目录 哈希 哈希(散列)函数 常见的哈希函数 字符串哈希函数 哈希冲突 闭散列(开放地址法) 开散列(链地址法/拉链法) 负载因子以及增容 对于闭散列 对于开散列结构 具体实现 哈希表(闭散列) ...
- 在C#中应用哈希表(Hashtable)
一,哈希表(Hashtable)简述 在.NET Framework中,Hashtable是System.Collections命名空间提供的一个容器,用于处理和表现类似key/value的键值对,其 ...
- 哈希表(hashtable)的javascript简单实现
javascript中没有像c#,java那样的哈希表(hashtable)的实现.在js中,object属性的实现就是hash表,因此只要在object上封装点方法,简单的使用obejct管理属性的 ...
- C#中哈希表(HashTable)的用法详解
1. 哈希表(HashTable)简述 在.NET Framework中,Hashtable是System.Collections命名空间提供的一个容器,用于处理和表现类似keyvalue的键值对, ...
- 哈希表 Hashtable c# 1613537346
哈希表 Hashtable c# 1613537346 使用命名空间 using System.Collections; 实例化得到对象 Hashtable 对象 = new Hashtable(); ...
- 三问了解哈希表和哈希冲突
什么是哈希表? 哈希表也叫散列表,它是基于数组的.这间接带来了一个优点:查找的时间复杂度为 O(1).当然,它的插入时间复杂度也是 O(1).还有一个缺点:数组创建后扩容成本较高. 哈希表中有一个&q ...
最新文章
- 在线抓图WebSnap Beta 1.2 更新
- NHibernate.Profiler 使用教程
- ajax 实时进度_如何做好项目进度管理?
- React Router路由详解
- 多继承以及MRO顺序【super().的使用】
- 在Linux中查看ftp状态,linux中ftp常见操作启动ftp状态,终止ftp会话
- android 滚动画画,Android利用ViewPager实现可滑动放大缩小画廊效果
- nuc8 黑苹果_萝莉的身材野兽的心——NUC8 MacOS黑苹果-Win10双系统体验
- 没有基础的人可以学python吗-没有任何基础的人,该如何学习Python?「附具体步骤」...
- JAVA对象属性方法的使用
- 案例7-1 模拟EXCEL排序 (25 分)(根据结构体某一元素排序)
- 微分比例控制与测速反馈控制
- 百度刷排名,刷流量,刷下拉软件【完全免费】胖虎图图-互动点击系统
- 苹果备忘录怎么使用计算机,原来苹果备忘录这么神奇!你还不会用?网友:万把块白花了...
- 安装arcgis api for python步骤、以及注意事项
- INF=0x3f3f3f3f是 什么意思?
- 【SeedLab】ARP Cache Poisoning Attack Lab
- 自学anaconda的正确姿势
- 无限循环抛出 No method found for class [B 这个异常
- Redis源码分析之unlock
热门文章
- java笔记--重定向输出流实现程序输出到日志
- Silverlight使用DataGrid的模板列(DataGridTemplateColumn)实现类似TreeListView控件的效果
- 一起来玩树莓派--解决复制文件时出现error opening file... permission denied问题
- NG Ng-template(模板元素)
- uefi linux开发环境,开发者为 Linux 添加了一系列 RISC-V UEFI 支持补丁
- 力扣225-用队列实现栈(C++,附思路及优化思路,代码)
- 力扣232-用栈实现队列(C++,附思路)
- jquery.treeview.js php mysql,jquery.treeview应用
- React 使用browserHistory项目访问404问题
- 【Java中级】(二)集合框架