前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >散列的基本概念

散列的基本概念

作者头像
全栈程序员站长
发布于 2022-08-28 02:27:48
发布于 2022-08-28 02:27:48
1.6K0
举报

大家好,又见面了,我是你们的朋友全栈君。

散列的基本概念

什么是散列?为什么需要散列?

散列是一种思想。与已经学过的其他数据结构相比较,向量是采用循秩访问(call by rank)的访问方式,列表是采用循位置访问(call by position)的访问方式,二叉搜索树是采用循关键码访问(call by key)的访问方式,散列与他们都不一样,是采用循值访问(call by value)的访问方式。

举个例子,你现在身处哪里也不是的场所的中央,四周一片沉默,仿佛全世界所有细雨落到全世界所有草坪上,这个时候你想回家了。沿世界上所有的街道一间一间房找过去,这是循秩访问;你记得你家是住在某省某市某街道多少号,然后你可以依次先到某省,再到某市,再到某条街道,然后找到你家,这是循关键码访问;而循值访问,则是你通常会采用的方法——你根本不用去回想我家的地址是多少,你知道它就在那里,就在家这个词刚刚出现在你的脑海中的时候。想到家乡,你想到的不是地址或者一串数字,而是一个生动的影像,包含它的环境,四周的风物,以及曾经的朋友。这就是循值访问

可以看到,相对于其他的访问方式,循值访问是将被访问对象的数值,与它在容器中的位置之间,直接建立了一个映射关系,从而对于任何对象的基本操作(访问,插入,删除)都只需要常数O(1)的时间,达到了最理想的境地。这就是人类需要散列的原因,你无法不被如此的诱惑所吸引。

完美散列

在时间与空间性能上均达到完美的散列,称为完美散列。

也就是说,对于完美散列,其中的每一个值,都可以唯一地映射到散列表中的一个位置,既无空余,亦无重复。从映射角度来看,完美散列是一个单射,同时也是一个满射。Bitmap就是完美散列的一个例子。

可以看出,完美散列实际中并不常见,在大多数的情形下,关键码的取值是远远大于词条的个数的,设关键码的取值为 [ 0 , R ) [0, R) [0,R), 词条的个数为 N N N,则 R > > N R >> N R>>N。设散列表的大小为 M M M,此时,从定义域 [ 0 , R ) [0, R) [0,R)到值域 [ 0 , M ) [0, M) [0,M)的映射不可能是单射,即不可避免地会出现不同的关键码映射到散列表中的同一个位置,即所谓冲突。因此就需要合理地选择这一个映射关系,即散列函数,使冲突出现的可能性最小;同时还应该事先约定好一旦出现这种冲突,应该采取的解决方案。这两个问题将在下面重点讨论,即散列函数的设计与冲突解决方案。

散列函数的设计

散列函数的设计方案?什么是好的散列函数?

前面提到,从词条空间到地址空间的映射,即散列函数,绝对不可能是单射,冲突是一定不可能避免的,但是好的散列函数应该保证尽可能地少出现冲突。由此,可以提炼出散列函数的几个设计指标。

  • 确定性。散列函数确定的条件下,同一个关键码应该总是映射到同一个地址,这样才满足一个函数的定义。
  • 快速性。是指散列地址的计算过程要尽可能快,要能在常数时间内完成。
  • 满射。好的散列函数最好是一个满射,这样可以充分利用散列空间,尽可能地减少冲突的发生。
  • 均匀性。也是为了减少冲突的发生,关键码被映射到各个散列地址的概率应该接近于 1 / M 1/M 1/M,这样可以防止汇聚(clustering)现象的发生,即关键码只被映射到少数的几个散列地址,在局部加剧散列冲突,全局的散列空间也没有得到充分地利用。

总之,为了保证冲突尽可能地少,散列函数越是随机,越是没有规律越好。

几个散列函数的实例

除余法(division method)

除余法的整体思路非常简单,即用关键码的值对散列表的长度 M M M取余,即 h a s h ( k e y ) = k e y m o d M hash(key) = key\ mod\ M hash(key)=key mod M,这样可以将关键码映射到整个散列空间上。

这里问题的关键在于散列表长度 M M M的选择。考虑有一组数据,其中的关键码以固定步长 S S S变化(实际中的数据往往就是这种形式的,而不是随机的,例如for循环一般就是固定步长的数据)。设第 i i i个数据第一次与第 j j j个数据的关键码发生冲突,即 S × i ≡ S × j ( m o d M ) S\times i \equiv S\times j\ (mod M) S×i≡S×j (modM) 即 S i Si Si与 S j Sj Sj是同余类,所以 S ( j − i ) ≡ 0 ( m o d M ) S(j – i) \equiv 0 \ (mod M) S(j−i)≡0 (modM) 由此可解得 j − i = M g c d ( M , S ) g c d f o r G r e a t e s t C o m m o n D i v i s o r j – i = \frac{M}{gcd(M, S)}\ gcd\ for\ Greatest\ Common\ Divisor j−i=gcd(M,S)M​ gcd for Greatest Common Divisor

根据上面对散列函数设计要求的分析,我们是希望散列函数可以尽可能地减少冲突,即这里的 j − i j – i j−i尽可能地大,就需要保证 M M M和 S S S的最大公因数要尽可能小,因此 M M M要和 S S S互质。但是由于散列表存储的不同数据具有不同的步长 S S S值,要使 M M M与所有可能的步长 S S S互质,只有当 M M M本身就是一个素数才可能实现。

MAD法(Multiply-add-divide method)

上面的除余法还存在着一些问题。

首先,除余法得到的散列地址,依然存在一定程度的连续性,即原来相邻的关键码对应的散列地址也仍然是相邻的;其次,在除余法中关键码较小的那些词条,始终被映射到散列表的起始区段,其中关键码为零的元素,其散列地址总是零,即是一个不动点,这显然违背了散列函数应该越随机,越没有规律越好的原则。MAD法正是对除余法上述问题的一个改进。

MAD法,顾名思义,就是对于关键码,依次执行乘法、加法和模余运算,即 h a s h ( k e y ) = ( a × k e y + b ) m o d M hash(key) = (a \times key + b)\ mod\ M hash(key)=(a×key+b) mod M

这样,只要适当选择 a , b a, b a,b,MAD法可以很好的克服除余法原有的连续性缺陷,其中参数 a a a的作用是使相邻关键码的散列地址更加分散, b b b的作用是作为一个偏移量,去掉不动点。

数字分析法

遵循散列函数越是随机没有规律,就越好的原则,引入了数字分析法,即对于关键码key的特定进制展开,抽取其中的几位,映射到一个散列地址。

比较简单的情形就是取十进制展开中的奇数位,作为散列地址,例如 h a s h ( 123456789 ) = 13579 hash(123456789) = 13579 hash(123456789)=13579

除此以外,还有平方取中法,即对于关键码 k e y key key取平方,然后截取中间的几位来作为散列地址。之所以选择中间的几位,是因为中间的几位是受到了原来的关键码更多数位的影响;相对于取高位数字(只受到原关键码高位数字影响)或者低位数字(只受到原关键码低位数字影响),取中间位数综合了更多位数的影响,因此随机性、均匀性更强,更不易出现冲突。

此外,还有折叠法往复折叠法

为了保证经过这些方法得到的值仍然落在散列空间以内,通常还都需要对散列表长度 M M M再取余。

随机数法

既然散列函数是随机性越强越好,那一个简明的思想是直接利用生成的伪随机数来构造散列地址。这样的话,任意一个伪随机数发生器本身就是一个好的散列函数了。即

h a s h ( k e y ) = r a n d ( k e y ) m o d M hash(key) = rand(key)\ mod\ M hash(key)=rand(key) mod M 其中, r a n d ( k e y ) rand(key) rand(key)是系统定义的第 k e y key key个伪随机数。

冲突解决方案

无论如何精心设计的散列函数,都不能完全地避免冲突的发生,随着数据量的增大,冲突的发生几乎是必然的。因此,就需要事先规定好冲突发生时的解决方案,从而保证散列表的正常工作。

封闭定址法(closed addressing)

多槽位法(multiple slots)

所谓冲突发生不过是不同的关键码被散列函数映射到同一个散列地址,既然如此,那我们事先为可能到来的、冲突的关键码预留一个位置不就可以了吗?这种简明的思想就是多槽位法

多槽位法就类似于一山二虎,将原来对应一个关键码的桶单元,划分为若干更小的槽位,从而可以容纳后续到来的冲突的关键码。

显而易见,这种方法具有致命的缺陷,即你永远也不知道槽位应该细分到何种程度,才能保证在任何情况下都够用。槽位划分太多的话,空间利用率会非常低;槽位划分不够,又不足以应对可能出现的冲突。此外,在极端条件下,当数据量非常大的时候,无论再多的槽位,也仍然有可能会产生溢出。

独立链法(separate chaining)

多槽位法所面临的问题,其实就是类似于数组这种静态数据结构所面临的问题,即在实际应用之前,你不会清楚数组的大小应该划分到多大。采用链表可以有效的解决数组空间不足的问题,而将链表应用到散列表的冲突解决方案,就成为了独立链法

独立链法多槽位法的核心思想是完全相同的,即预备空间来应对可能出现的冲突情况。不过与多槽位法不同,独立链法是将所有冲突的关键码组织成一个列表,利用列表的动态增长特性,来规避预备的冲突空间不足的问题。

公共溢出区法(overflow area)

基本思想与上面两个也是相同的,即在事先预备公共的溢出区,来存储关键码冲突的词条。

上面几种方法都具有相同的思想,即在原有的散列表外还预备额外的空间来存储词条,此时散列地址不仅仅局限于散列表所覆盖的范围内,还包括这个额外的存储冲突词条的空间,故也称作开散列(open hashing),或者封闭定址法(closed addressing),因为任一给定的词条只可能存储在某一确定的桶单元,其他的桶单元对该词条是不开放的。

开放定址法(open addressing)

线性试探法(linear probing)

线性试探法是指,在插入关键码 k e y key key时,若发现第 h a s h ( k e y ) hash(key) hash(key)个桶空间已被占用,则继而试探它的下一个桶空间,如此不断,直到发现一个可用的空桶。

线性试探法的问题在于,随着散列表装填因子的增大,散列表中的查找链也会随之增长,从而降低了散列表的查找性能。另一方面,采用线性试探法时,一旦在某一局部发生冲突,极有可能后续的插入会在这里引发更多的冲突,并且多组各自冲突的查找链有可能相互重叠。

但如果散列表的装填因子不大 λ < 0.5 \lambda < 0.5 λ<0.5,采用线性试探法的散列表的平均效率,通常都可保持在较为理想的水平。并且线性试探法中的词条,具有良好的局部性,可以极大地降低I/O操作的开销。

单向平方试探法(quadratic probing)

平方试探法可以在一定程度上缓解上述的冲突聚集现象。在试探过程中发生第 j j j次发生冲突时,转而试探 ( h a s h ( k e y ) + j 2 ) m o d M , j = 0 , 1 , 2 , ⋯ (hash(key) + j^2) \ mod \ M, j = 0, 1, 2, \cdots (hash(key)+j2) mod M,j=0,1,2,⋯ 由于各次试探的位置到起始位置的距离,以平方速率增长,故称为平方试探法

线性试探法比较,平方试探法可以理解为迅速离开发生冲突的“是非之地”,以平方的速率迅速跳离关键码聚集的区段,因此可以在一定程度上缓解汇聚的现象。

可是,关于平方试探法,我们不难提出一些问题,比如平方试探法果真可以覆盖整个散列表吗?是否存在散列表本来有空桶,却无法被探测到的现象?

这种情况是存在的,可以自己举一些例子要验证一下。不过,只要散列表长度 M M M为素数,并且装填因子 λ ≤ 0.5 \lambda \le 0.5 λ≤0.5,则平方试探法迟早必然会终止于某个空桶,即 n 2 m o d M n^2 \ mod\ M n2 mod M的取值恰好有 c e i l ( M 2 ) ceil(\frac{M}{2}) ceil(2M​)种,并且恰好由查找链的前 c e i l ( M 2 ) ceil(\frac{M}{2}) ceil(2M​)取遍。以下给出证明:

设 a < b < c e i l ( M 2 ) a < b < ceil(\frac{M}{2}) a<b<ceil(2M​),并且第 a a a次试探与第 b b b次试探冲突,即 a 2 , b 2 a^2, b^2 a2,b2是 M M M的同余类

a 2 ≡ b 2 ( m o d M ) a^2 \equiv b^2\ (mod\ M) a2≡b2 (mod M) 所以 b 2 − a 2 ≡ 0 ( m o d M ) b^2 – a^2 \equiv 0\ (mod\ M) b2−a2≡0 (mod M) 即 ( b − a ) ( b + a ) (b – a)(b + a) (b−a)(b+a)可以整除 M M M。但是由于 b − a < M , b + a < M b – a < M, b + a < M b−a<M,b+a<M,如果 ( b − a ) ( b + a ) (b-a)(b+a) (b−a)(b+a)可以整除 M M M的话,则 M M M必有不为一的因子,与 M M M为素数矛盾,所以前 c e i l ( M 2 ) ceil(\frac{M}{2}) ceil(2M​)次试探必不冲突,故得证。

双向平方试探法

根据上面的分析,在 M M M为素数并且装填因子 λ < 0.5 \lambda < 0.5 λ<0.5的时候,单向平方试探法可以保证试探必然终止。一个自然的想法是,另一半的桶,是否也可以利用起来呢?这就是双向平方探测法

双向平方探测法,就是在发生冲突时,依次向前向后进行平方探测,即 ( h a s h ( k e y ) ± j 2 ) m o d M , j = 0 , 1 , 2 , ⋯ (hash(key) \pm j^2)\ mod \ M, j = 0, 1, 2, \cdots (hash(key)±j2) mod M,j=0,1,2,⋯

这种方法看似是把两侧的桶都利用起来了,但是我们也不禁产生一个问题,即这双向的探测是否是相互独立呢?它们之间除了零,是否还有其他公共的桶?

答案是,是存在不独立的情况的,并且这种情况还相当的多,也可以自己举几个例子来看一下。但是,如果散列表的长度取做素数,并且 M = 4 k + 3 M = 4k + 3 M=4k+3,则必然可以保证查找链的前 M M M项都是互异的,以下来证明这个结论。

这里,我们首先需要提到费马的双平方定理,即任意素数 p p p可以表示为两个正整数的平方和,当且仅当 p = 4 k + 1 p = 4k + 1 p=4k+1。

在此基础上,如果我们可以注意到 ( u 2 + v 2 ) ( s 2 + t 2 ) = ( u s + v t ) 2 + ( u t − v s ) 2 (u^2 + v^2)(s^2 + t^2) = (us + vt)^2 + (ut – vs)^2 (u2+v2)(s2+t2)=(us+vt)2+(ut−vs)2 即任意两个可以表示为两个正整数的平方和的正整数的乘积,也可以表示为两个正整数的平方和。 就可以推知,任意自然数 n n n可以表示为一对整数的平方和,当且仅当在其素分解中,形如 M = 4 k + 3 M = 4k + 3 M=4k+3形式的每一个素因子均为偶数次方。这个推导的过程要知道啊,我这里就不写了。

从而我们假设对于 a < c e i l ( M 2 ) , b < c e i l ( M 2 ) a < ceil(\frac{M}{2}), b < ceil(\frac{M}{2}) a<ceil(2M​),b<ceil(2M​),并且 − a 2 -a^2 −a2与 b 2 b^2 b2是同余类,即 − a 2 ≡ b 2 ( m o d M ) -a^2 \equiv b^2 \ (mod \ M) −a2≡b2 (mod M) 所以 a 2 + b 2 ≡ 0 ( m o d M ) a^2 + b^2 \equiv 0 \ (mod \ M) a2+b2≡0 (mod M) 即 a 2 + b 2 a^2 + b^2 a2+b2可以整除 M M M。由于 M = 4 k + 3 M = 4k + 3 M=4k+3且为素数,所以 a 2 + b 2 a^2 + b^2 a2+b2可以整除 M 2 M^2 M2,但是 a 2 + b 2 < M 2 a^2 + b^2 < M^2 a2+b2<M2,所以假设不成立,故原命题得证。

随机试探法(pseudo-random probing)

仿照散列函数中的随机数法,在发生冲突时也可以采用随机数发生器来确定试探的位置,就是随机试探法

再散列法(double hashing)

顾名思义,设置一个二级散列函数来确定试探位置,即 [ h a s h ( k e y ) + j × h a s h 2 ( k e y ) ] m o d M [hash(key) + j \times hash_2(key)] \ mod\ M [hash(key)+j×hash2​(key)] mod M 其他的方法其实都可以视作再散列法的一个实例。

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/146521.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022年5月1,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
【数据结构与算法】哈希表实现:闭散列 && 开散列
​ 顺序结构以及平衡树中,元素关键码与其存储位置之间没有对应的关系,因此在 查找一个元素时,必须要经过关键码的多次比较。顺序查找时间复杂度为 O(N),平衡树中为树的高度,即 O(
利刃大大
2025/05/01
590
【数据结构与算法】哈希表实现:闭散列 && 开散列
哈希表基本概念介绍及哈希冲突的处理方法(附源码)
  哈希表(散列表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做哈希(散列)函数,存放记录的数组叫做哈希(散列)表。
嵌入式与Linux那些事
2021/05/20
9390
哈希表基本概念介绍及哈希冲突的处理方法(附源码)
2019Java面试题:为什么使用hashmap需要重写hashcodes和equals方法?
总的来说,Java中的集合(Collection)有两类,一类是List,再有一类是Set。你知道它们的区别吗?前者集合内的元素是有序的,元素可以重复;后者元素无序,但元素不可重复。那么这里就有一个比较严重的问题了:要想保证元素不重复,可两个元素是否重复应该依据什么来判断呢?这就是Object.equals方法了。但是,如果每增加一个元素就检查一次,那么当元素很多时,后添加到集合中的元素比较的次数就非常多了。也就是说,如果集合中现在已经有1000个元素,那么第1001个元素加入集合时,它就要调用1000次equals方法。这显然会大大降低效率。
葆宁
2019/04/19
9510
2019Java面试题:为什么使用hashmap需要重写hashcodes和equals方法?
散列表的相关概念
HashMap是Java源码中非常优秀的源码,涉及到很多的概念,算法、红黑树、数组、链表... 之前也尝试过硬着头皮去学习,但是由于基础本身就不是很牢固,所以后面也没有多少收获。那么,这次笔者先来梳理一下HashMap的一些概念。
飞翔的竹蜻蜓
2020/07/08
7230
散列表的相关概念
算法导论第十一章 散列表
一、散列表的概念 本章介绍了散列表(or hash table)的概念、散列函数的设计及哈希冲突的处理。散列表(为了形象描述,我们通常叫槽)从表意上看是一种数据结构,但把它归为算法思想更为贴切。对于大部分的查找问题,使用散列表能达到O(1)的效率。现在很多大公司在面试大数据的题目时,解决方案里绝对少不了散列表的思想,例如百度的一道面试题:Top K查找问题: 问题描述: 搜索引擎会通过日志文件把用户每次检索使用的所有检索串都记录下来,每个查询串的长度为1-255字节。 假设目前有一千万个记
Linux云计算网络
2018/01/11
1.1K0
Hash算法的讲解[通俗易懂]
散列表,又叫哈希表,它是基于快速存取的角度设计的,也是一种典型的“空间换时间”的做法。顾名思义,该数据结构可以理解为一个线性表,但是其中的元素不是紧密排列的,而是可能存在空隙。
全栈程序员站长
2022/09/20
2.3K0
Hash算法的讲解[通俗易懂]
【C++】哈希表 --- 闭散列版本的实现
哈希表(Hash Table)是一种数据结构,它通过哈希函数将键映射到表中的一个位置来访问记录,支持快速的插入和查找操作。
叫我龙翔
2024/07/16
1800
【C++】哈希表 --- 闭散列版本的实现
【C++】哈希
顺序结构以及平衡树 中,元素关键码与其存储位置之间没有对应的关系,因此在 查找一个元素
青衫哥
2023/04/23
3750
【C++】哈希
由散列表到BitMap的概念与应用(一)
散列表是种数据结构,它可以提供快速的插入操作和查找操作。第一次接触散列表时,它的优点多得让人难以置信。不论散列表中有多少数据,插入和删除只需要接近常量的时间即O(1)的时间级。实际上,这只需要几条机器指令。
aoho求索
2018/12/05
2.2K0
【C++的剃刀】我不允许你还不会用哈希~
,即最差情况下需要比较红黑树的高度次,当树中的节点非常多时,查询效率也不理想。最好的查询是,进行很少的比较次数就能够将元素找到,因此在C++11中,STL又提供了4个unordered系列的关联式容器,这四个容器与红黑树结构的关联式容器使用方式基本类似,只是其底层结构不同
小文要打代码
2024/10/16
1430
【C++的剃刀】我不允许你还不会用哈希~
【408&数据结构】散列 (哈希)知识点集合复习&考点题目
散列查找是一种高效的查找方法,它通过散列函数将关键字映射到数组的一个位置,从而实现快速查找。这种方法的时间复杂度平均为(O(1)),但最坏情况下可能会退化到(O(n))。
苏泽
2024/09/09
2980
【408&数据结构】散列 (哈希)知识点集合复习&考点题目
OJ刷题记录:散列查找实验
题目描述: 请设计一个整型闭散列表,散列函数为除留余数法,处理冲突时的探查方法为线性探查法,其中散列表的长度、除留余数法的模和关键码的个数由键盘输入,再根据输入由键盘输入所有的关键码。分别对三个待查值在散列表中进行查找,如果找到了输出位置,如果没找到,输出“none”并把该待查值插入到散列表中,如果散列表满输出“full”。
英雄爱吃土豆片
2020/11/12
6100
散列表(一):散列表概念、 散列函数构造方法、 常见字符串哈希函数(测试冲突)
一、散列表基本概念 1、散列表(hash table) ,也叫哈希表,是根据关键码而直接进行访问的数据结构。也就是说,它通过把关键码映射到表中一个位置 来访问记录,以加快查找的速度。这个映射函数叫做散
s1mba
2017/12/28
2.2K0
散列表(一):散列表概念、 散列函数构造方法、 常见字符串哈希函数(测试冲突)
学生物的女朋友都能看懂的哈希表总结!
之前给大家介绍了链表,栈和队列今天我们来说一种新的数据结构散列(哈希)表,散列是应用非常广泛的数据结构,在我们的刷题过程中,散列表的出场率特别高。所以我们快来一起把散列表的内些事给整明白吧,文章框架如下。
公众号袁厨的算法小屋
2020/11/25
8740
学生物的女朋友都能看懂的哈希表总结!
《大话数据结构》 查找 以及一个简单的哈希表例子
第八章 查找 定义:查找就是根据给定的某个值,在查找表中确定一个其关键字等于给定值的数据元素(或记录)。 8.2 查找概论 查找表(Search table):是由同一类型的数据元素构成的集合。 关键字(key):是数据元素中某个数据项的值,又称为键值。 若此关键字可以唯一的标识一个记录,则称此关键字为主关键字(Primary key)。 对于那些可以识别多个数据元素的关键字,我们称为次关键字(Secondary key)。 查找表按照操作方式来分有两大种:静态查找表和动态查找表 静态查找表(Static
xcywt
2018/03/28
2.4K0
《大话数据结构》 查找 以及一个简单的哈希表例子
解析hash(散列)数据结构
在学习完map、set这两个由红黑树构成的容器后,我们来到了这里hash,首先我们要有一个基础的认知——哈希和map与set的仅在使用时的差别区别:前者内部的元素没有序,而后者有序,其它的都相同,这里我们可以通过STL标准库对应的unordered_map和unordered_set的两个名字就能看出,那hash存在的意义在哪里?底层的数据结构又是如何实现的呢?
比特大冒险
2023/04/16
8160
解析hash(散列)数据结构
C++: unordered系列关联式容器
在C++98中,STL提供了底层为红黑树结构的一系列关联式容器,在查询时效率可达到
用户11317877
2024/10/16
1010
C++: unordered系列关联式容器
【数据结构】哈希表
顺序结构以及平衡树中,元素关键码与其存储位置之间没有对应的关系,因此在查找一个元素时,必须要经过关键码的多次比较。顺序查找时间复杂度为 $O(N)$ ,平衡树中为树的高度,即 $O(logN)$ ,搜索的效率取决于搜索过程中元素的比较次数。
椰椰椰耶
2024/09/20
1190
【数据结构】哈希表
【数据结构】什么是哈希表(散列表)?
在正式开始深入了解哈希表之前呢, 我想带大家先回忆一下生活中咱们的这个"老朋友"。可能你会感到诧异, 我怎么会和它是"老朋友"呢? 别急, 其实你的生活中常常会出现哈希的身影,只是你没有细心观察罢了,不信你看下面几个场景对你来说是不是非常熟悉呢:
修修修也
2024/10/06
2730
【数据结构】什么是哈希表(散列表)?
数据结构:查找
衡量标准:查找过程中对关键字的平均比较次数——平均查找长度ASL。设查找到第i个元素的概率为p,比较次数为c,则查找成功的ASL_{succ}=\sum^n_{i=1}p_ic_i
ttony0
2022/12/26
9920
数据结构:查找
相关推荐
【数据结构与算法】哈希表实现:闭散列 && 开散列
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档