散列表(算法导论笔记)

散列表

直接寻址表

         一个数组T[0..m-1]中的每个位置分别对应全域U中的一个关键字,槽k指向集合中一个关键字为k的元素,如果该集合中没有关键字为k的元素,则T[k] = NIL

全域U={0,1,…,9}中的每个关键字都对应于表中的一个下标值,由实际关键字构成的集合K={2,3,5,8}决定表中的一些槽,这些槽包含指向元素的指针,而另一些槽包含NIL 

        

         直接寻址的技术缺点非常明显:如果全域U很大,则在一台标准的计算机可用内存容量中,要存储大小为|U|的一张表T也许不太实际,甚至是不可能的。还有,实际存储的关键字集合K相对于U来说可能很小,使得分配给T的大部分空间都将浪费掉,此时可以使用散列表改进。

散列表

         在直接寻址方式下,具有关键字k的元素被存放在槽k中,在散列方式下,该元素存放在h(k)中;即利用散列函数h,由关键字k计算出槽的位置,这里,函数h将关键字的全域U映射到散列表T[0,..,m-1]的槽位上。

h : U     {0,…,m-1}

         这里散列表的大小m一般要比|U|小得多,可以说一个具有关键字k的元素被散列到槽h(k)上

 

 

         这里存在一个缺点:两个关键字可能映射到同一个槽中,这种情形称为冲突,解决冲突的方法有很多。

解决冲突

链接法

         在链接法中,把散列到同一槽中的所有元素都放在一个链表中,如下图所示,槽j中有一个指针,它指向存储所有散列到j的元素的链表的表头;如果不存在这样的元素,槽j中为NIL

 

链表可以是单链表,但双链表的删除操作会更快

分析(查找一个关键字)

         给定一个能存放n个元素的,具有m个槽位的散列表T,定义T的装载因子α为n/m,即一个链表的平均存储元素数量,α可以大于,等于或者小于1.

         用链接法散列的最坏情况性能很差:所有的n个关键字都散列到同一个槽中,从而产生一个长度为n的链表,这时,最坏情况下查找时间为θ(n),再加上计算散列函数的时间,如果就和用一个链表来链接所有的元素差不多了。

         散列方法的平均性能依赖于所选取的散列函数h,将所有关键字集合分布在m个槽位上的均匀程度。

         在平均情况下,查找一个关键字有两个结果:查找成功和查找不成功。

         在简单均匀散列的情况下,任何尚未被存储在表中的关键字k都等可能地被散列到m个槽中的任何一个,因此,当查找一个关键字k时,在不成功的情况下,查找的期望时间就是查找到链表T[h(k)]末尾的期望时间,这一时间的期望长度为α,于是一次不成功的查找平均要检查α个元素,并且所需要的总时间(包括计算h(k)的时间)为θ(1+α)

         在查找成功的情况下,平均需要的时间也是θ(1+α),具体证明参考《算法导论》中文版146页。

总结

         上述的分析意味着,如果散列表中槽数至少与表中的元素成正比(比如说,当要散列的元素的数量增加时,散列表T的槽数也要保持同样比例的增长),则有

n = Ο(m),从而α= n/m = Ο(m) / m = Ο(1),所以查找操作平均时间需要常数时间。如果散列的元素的数量增加了,但是散列表的槽数没有增长,此时n = Ο(m)就不成立,散列表的操作时间就和之前的不一样了。

开放寻址法

         在开放寻址法中,所有的元素都存放在散列表中,也就是说,每个表项或包含动态集合的一个元素,或包含NIL。当查找某个元素时,要系统地检查所有的表项,知道查找到所需要的元素,或者最终查明该元素不在表中。不像链接法,这里既没有链表,也没有元素存放在散列表外,因此在开放寻址法中,散列表可能会被填满,以至于不能插入任何新的元素,因此装载因子α = n / m绝对不会超过1,也就是说,要散列的元素绝对不会多于槽的数量。

探查方式

线性探查

         给定一个普通的散列函数 h ' : U → { 0, 1, …, m - 1 }(称为辅助散列函数),线性探查方法采用的散列函数为:

                   h ( k , i ) = ( h '( k ) + i ) mod m , i = 0, 1, …, m - 1

         给定一个关键字 k ,第一个探查的槽是 T [ h '( k ) ],亦即,由辅助散列函数所给出的槽。接下来探查的是槽 T [ h ' ( k ) + 1 ], …,直到槽 T [ m - 1 ],然后又绕到槽 T [ 0 ], T [ 1 ], …直到最后探查槽 T [ h ' ( k ) - 1 ]。在线性探查方法中,初始探查位置确定了整个序列,故只有 m 种不同的探查序列。

         线性探查方法很容易实现,但它存在一个问题,称作一次群集。随着时间的推移,连续被占用的槽不断增加,平均查找时间也随着不断增加。群集现象很容易出现,这是因为当一个空槽前有 i 个满的槽时,该空槽作为下一个将被占用槽的概率是( i + 1 ) / m 。连续被占用槽的序列将会越来越长,因而平均查找时间也会随之增加。

二次探查

         二次探查采用如下形式的散列函数:

                            h ( k , i ) = ( h ' ( k ) + c1 i + c2i2) mod m

         其中 h '是一个辅助散列函数, c1 和 c2 为辅助常数(不等于0), i = 0, 1, …, m - 1。初始的探查位置为 T [ h '( k ) ],后续的探查位置要在此基础上加上一个偏移量,该偏移量以二次的方式依赖于探查号 i 。这种探查方法的效果要比线性探查好很多,但是,如果两个关键字的初始探查位置相同,那么他们的探查序列也是相同的,这是因为 h ( k1 , 0 ) = h ( k2 , 0 )蕴含着 h ( k1 , i ) = h ( k2 , i )。这一性质可导致一种程度较轻的群集现象,称为 二次群集。二次探查也只有 m 个不同的探查序列。

双重散列

         双重散列 是用于开放寻址法的最好方法之一,它采用如下形式的散列函数:

h ( k , i ) = ( h1 ( k ) + i h2 ( k ) ) mod m

         其中 h1 和 h2 为辅助散列函数。初始探查位置为 T [ h1 ( k ) ],后续的探查位置在此基础上加上偏移量 h2 ( k )模 m 。

         为能查找整个散列表,值 h2 ( k )要与表的大小 m 互质。确保这个条件成立的一种方法是取 m 为 2 的幂,并设计一个总产生奇数的 h2 。另一种方法是取 m 为质数,并设计一个总是产生较 m 小的正整数的函数 h2 。例如,可以取m为素数,m略小于m,如下:

h1 ( k ) = k mod m,   h2 ( k ) = 1 + ( k mod m)

         双重散列法中用了 Θ ( m2 )中探查序列。

分析

         相对于链接法,开放寻址法的好处在于不需要用到指针,而是计算出槽的序列,于是,不用存储指针而节省的空间,使得可以用同样地空间来提供更多的槽,潜在地减少了冲突,提高了检索速度。

         从开放寻址法的散列表中删除元素比较困难,当从槽i中删除关键字时,不能仅仅将NIL置于其中来标识它为空,如果这样做就会出现问题:在插入关键字k时,发现槽i被占用了,则k会插入到后面的位置上;此时将槽i中的关键字删除后,就无法检索到关键字k了,有一个解决方法就是,在槽i中置一个特定的值DELETED替代NIL来标记空槽。当使用特殊的值DELETED时,查找时间就不再依赖于装载因子了,为此,在必须删除关键字的应用中,更常见的方法是采用链接法来解决冲突。

         给定一个装载因子为α = n / m,α<1,的开放寻址散列表,并假设是均匀散列的,则对于一次不成功的查找,其期望的探查次数至多为1 / (1 – α)。具体证明参考《算法导论中文版》P155.

         1/(1 - α) = 1 + α + α2 + α3 + … 这个界有一个直观的解释,无论如何,总要进行第一次探查,第一次探查发现的是一个已经占用的槽时,必须要进行第二次探查,进行第二次探查的概率大约为α,前两次探查所发现的槽均已被占用时,进行第三次探查的概率大约为α2,等等

         如果α是一个常数,一次不成功查找的运行时间为Ο(1),例如,如果散列表一半是满的,一次不成功查找的平均探查次数至多为1 / ( 1- 0.5) = 2,如果散列表90%是满的,则平均探查次数至多为1 / ( 1 – 0.9) = 10

         假设采用的是均匀散列,平均情况下,向一个装载因子为α的开放寻址散列表中插入一个元素之多需要做1/(1 - α)次探查,因为插入一个关键字首先要做一次不成功查找,所以插入元素的时间和一次不成功的探查时间相同。

         对于一个装载因子为α<1的开放寻址散列表,一次成功查找中的探查期望数至多为(1/α)ln(1/(1-α))。具体证明参考《算法导论中文版》P155.如果散列表是半满的,则一次成功的探查中,探查的期望数小于1.39,如果散列表为90%满的,则探查的期望数小于2.56

散列函数

除法散列法

         在 除数散列法 中,通过取 k 除以 m 的余数,来将关键字 k 映射到 m 个槽的某一个中去。亦即,散列函数为:

h ( k ) = k mod m

         当应用除数散列时,要注意 m 的选择,可选的 m 值通常是与 2 的整数幂不太接近的质数。

乘法散列法

         乘法散列法 包含两个步骤。第一步,用关键字 k 乘上常数 A (0 < A < 1),并抽取出 k A 的小数部分。然后,用 m 乘以这个值,再向下取整。散列函数为:

h ( k ) = floor( m ( k A mod 1 ))

         floor()函数为向下取整的意思。乘法方法的一个优点是对 m 的选择没有什么特别的要求,一般选择它为 2 的幂( m = 2p , p 为某个整数)。

         例如:h ( k ) = ( A * k mod 2w) rsh (w – r),其中w为计算机的位数(32或者64位),m为槽的数量,rsh(w – r)意思为向右移位w – r 位,A是一个奇数,并且2w-r < A < 2w,m = 2r

全域散列法

         任何的散列函数都可能出现最坏情况性态,即 n 个关键字都散列到同一个槽中,使得平均的检索时间为 Θ ( n ):唯一有效的改进方法是随机地选择散列函数,使之独立于要存储的关键字。这种方法称作全域散列。

         全域散列的基本思想是在执行开始时,就从一组仔细设计的函数中,随机地选择一个作为散列函数。随机化保证了没有哪一种输入会始终导致最坏情况性态。同时,随机化使得即使是对同一个输入,算法在每一次执行时的性态也是不一样的。这样就可以确保对于任何输入,算法都具有良好的平均情况性态。

         设 H 为有限的一组散列函数,它将给定的关键字域 U 映射到{ 0, 1, …, m - 1 }。这样的一组函数称为是 全域的 ,如果对每一对不同的关键字 k , l ∈ U ,满足 h ( k ) = h ( l )的散列函数 h ∈ H 的个数至多为 | U | / m 。换言之,如果从 H 中随机选择一个散列函数,当关键字 k ≠ l 时,两个发生碰撞的概率不大于 1 / m ,这也正是从集合{ 0, 1, …, m - 1 }中随机地,独立地选择 h ( k )和 h ( l )时发生碰撞的概率。

         如果 h 选择一组全域的散列函数,并用于将 n 个关键字散列到一个大小为 m 的,用链接法解决碰撞的表 T 中。如果关键字 k 不在表中,则 k 被散列至其中的链表的期望长度E[ nh(k) ]至多为 α 。如果关键字 k 在表中,则包含关键字 k 的链表的期望长度E[ nh(k) ]至多为 1 + α 。

         对于一个具有 m 个槽位的表,利用全域散列和链接法解决碰撞,需要 Θ ( n )的期望时间来处理任何包含了 n 个 INSERT, SEARCH , DELETE 操作的操作序列,该序列中包含了 O ( m )个 INSERT 操作。

设计一个全域散列函数类

  1.选择一个素数,用m表示

  2.把k分解成r+1位的数,k = <k0, k1, k2, … kr>    0 <= kr <= m-1

  3.选择一个数a = <a0 , a1 , … , ar >,每一个ai都从{0, 1, … , m-1}中随机选择

  4.Ha ( k ) = mod m

完全散列

         可以采用两级的散列方法来设计完全散列方案,在每级上都使用全域散列,如下图所示:

外层的散列函数为h(k) = ((ak + b) mod p)mod m,一个二级散列表Sj中存储了所有散列到槽j中的关键字,其大小为mj=nj2,并且相关的散列函数为hj(k) = ((ajk + bj)mod p)mod mj

 

         第一级与带连接的散列表基本上一样:利用从某一全域组中仔细选出的一个散列函数h,将n个关键字散列到m个槽中。

         然而,采用一个较小的二次散列表Sj,及相关的散列函数hj,而不是将散列到槽j中的所有关键字建立一个链表,利用精心选择的散列函数hj,可以确保在第二级上不出现冲突。

         为了确保第二级上不出现冲突,需要让散列表Sj的大小mj为散列到槽j中的关键字数nj的平方,尽管mj对nj的这种二次依赖看上去可能使得总体存储需求很大,但是可以通过适当地选择第一级散列函数,可以将预期使用的总体存储空间限制为Ο(n)。

二级散列冲突的概率

         如果从一个全域散列函数类中随机选出散列函数h,将n个关键字存储在一个大小为m = n2的散列表中,那么表中出现冲突的概率小于1/2

         上述定理的意思:对于一个从H中随机选出的散列函数h,较有可能不会发生冲突,给定待散列的包含n个关键字的集合K,只需要几次随机的尝试,就能比较容易地找出一个没有冲突的散列函数h。

完全散列所需空间

         如果从某一个全域散列函数类中随机选出散列函数h,用它将n个关键字存储到一个大小为m = n的散列表中,并将每个二次散列表的大小设置为mj = nj2,则在一个完全散列方案中,存储所有二次散列表所需要的存储总量的期望值小于2n

 

转载于:https://www.cnblogs.com/eagle159/p/3875502.html

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

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

相关文章

Python的os模块常用文件夹的增删改查详解

python常用os模块增os.makedirs("path\\目录") 用于递归创建目录删os.remove("path")用于删除指定路径&#xff08;path&#xff09;的文件os.rmdir("path")&#xff0c;用于删除指定路径&#xff08;path&#xff09;的目录改os.rename() 方法用…

Python中常用的Python time模块常用函数

常用函数time.time()函数time.localtime() 函数time.mktime()函数time.strftime() 函数time.strptime() 函数time.sleep() 函数https://blog.csdn.net/hanhanwanghaha宝藏女孩 欢迎您的关注&#xff01; 欢迎关注微信公众号&#xff1a;宝藏女孩的成长日记 如有转载&#xff0c…

SyntaxError: ‘return‘ outside function 在python里面的报错问题

https://blog.csdn.net/hanhanwanghaha宝藏女孩 欢迎您的关注&#xff01; 欢迎关注微信公众号&#xff1a;宝藏女孩的成长日记 如有转载&#xff0c;请注明出处&#xff08;如不注明&#xff0c;盗者必究&#xff09; Return需要放在函数里面 报错情况&#xff1a; 报错代码&…

python中的以简单例子解释函数参数、函数定义、函数返回值、函数调用

python-函数1.函数定义2.自定义函数&#xff0c;基本规则3.语法4.参数4.1必备参数4.2默认参数4.3不定长参数4.4匿名参数5.函数举例代码1.函数定义 函数是组织好的&#xff0c;可重复使用的&#xff0c;用来实现功能的代码段。 2.自定义函数&#xff0c;基本规则 1.以 def 关…

洛谷 P1330 封锁阳光大学题解

题目描述 曹是一只爱刷街的老曹&#xff0c;暑假期间&#xff0c;他每天都欢快地在阳光大学的校园里刷街。河蟹看到欢快的曹&#xff0c;感到不爽。河蟹决定封锁阳光大学&#xff0c;不让曹刷街。 阳光大学的校园是一张由N个点构成的无向图&#xff0c;N个点之间由M条道路连接。…

Python中的for i in range(range()函数的for循环)如何使用,详细介绍

range函数的for循环1.定义2.两种形式3.可理解性例子4.range函数的特性详述4.1 左闭右开4.2 开始值默认为04.3 步长值默认为14.4 range函数的反向输出5.与列表list的使用6.range与list的区别1.定义 range是一个函数&#xff0c;它返回的是一个可迭代对象&#xff0c;大多使用于…

SyntaxError: invalid syntax-python报错

https://blog.csdn.net/hanhanwanghaha宝藏女孩 欢迎您的关注&#xff01; 欢迎关注微信公众号&#xff1a;宝藏女孩的成长日记 让这个可爱的宝藏女孩在努力的道路上与你一起同行&#xff01; 如有转载&#xff0c;请注明出处&#xff08;如不注明&#xff0c;盗者必究&#xf…

洛谷 P2921 在农场万圣节Trick or Treat on the Farm题解

题意翻译 题目描述 每年&#xff0c;在威斯康星州&#xff0c;奶牛们都会穿上衣服&#xff0c;收集农夫约翰在N(1<N<100&#xff0c;000)个牛棚隔间中留下的糖果&#xff0c;以此来庆祝美国秋天的万圣节。 由于牛棚不太大&#xff0c;FJ通过指定奶牛必须遵循的穿越路线来…

TypeError: can‘t send non-None value to a just-started generator-python报错问题

https://blog.csdn.net/hanhanwanghaha宝藏女孩 欢迎您的关注&#xff01; 欢迎关注微信公众号&#xff1a;宝藏女孩的成长日记 如有转载&#xff0c;请注明出处&#xff08;如不注明&#xff0c;盗者必究&#xff09; TypeError: can’t send non-None value to a just-starte…

Redux概览

简介 Redux 是一个有用的架构Redux 的适用场景&#xff1a;多交互、多数据源工作流程图 action 用户请求 //发出一个action import { createStore } from redux; const store createStore(fn);//其中的type属性是必须的&#xff0c;表示 Action 的名称。其他属性可以自由设置 …

详解python中的yield(生成器)

yield1.yield与return的异同点2.yield使用的好处3.具体实例理解生成器的用法实例一实例二实例三1.yield与return的异同点 相同&#xff1a;都可以返回函数执行的结果 不同点&#xff1a; return是用来返回具体的某个值。 yield一般与循环一起用&#xff0c;被称之生成器&…

洛谷 P1219 八皇后题解

题目描述 检查一个如下的6 x 6的跳棋棋盘&#xff0c;有六个棋子被放置在棋盘上&#xff0c;使得每行、每列有且只有一个&#xff0c;每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子。 上面的布局可以用序列2 4 6 1 3 5来描述&#xff0c;第i个数字表示在第i行的相…

python不定长参数详解

不定长参数1.不定长参数两种基本形式&#xff1a;2.实例2.1实例一2.2实例二2.3实例三2.4实例四2.5实例五https://blog.csdn.net/hanhanwanghaha宝藏女孩 欢迎您的关注&#xff01; 欢迎关注微信公众号&#xff1a;宝藏女孩的成长日记 如有转载&#xff0c;请注明出处&#xff0…

TypeError: missing 1 required keyword-only argument-python中的报错问题

如下图所示&#xff0c;报错为TypeError: JayChou() missing 1 required keyword-only argument: ‘c’ 翻译过来是&#xff1a;TypeError:JayChou&#xff08;&#xff09;缺少1个仅限关键字的参数&#xff1a;“c” 报错代码&#xff1a; #codingutf-8def JayChou(a, *b, …

xshell下载及连接Linux

xshell的下载及连接Linux一、下载二、连接Linux一、下载 网址&#xff1a;https://www.netsarang.com/zh/xshell/ 1进入主页后下滑点击下载 2.不要去安装盗版&#xff0c;会出现一些问题。选择家庭版即可&#xff0c;点击免费授权页面 3.填上你的姓名和邮件&#xff0c;再勾…

xshell更改背景颜色,解决乱码问题及更改字体及字体大小

https://blog.csdn.net/hanhanwanghaha宝藏女孩 欢迎您的关注&#xff01; 欢迎关注微信公众号&#xff1a;宝藏女孩的成长日记 如有转载&#xff0c;请注明出处&#xff08;如不注明&#xff0c;盗者必究&#xff09; 这三个键分别是更改背景颜色&#xff0c;解决乱码问题&…

PL/SQL Developer的错误提示弹框的文本显示乱码问题

问题&#xff1a;Windows中文环境下&#xff0c;PL/SQL Developer的错误提示弹框文本为乱码&#xff0c;如下&#xff1a; 解决过程&#xff1a;1.使用SELECT * FROM v$nls_parameters;查询得知服务器的字符集编码为&#xff1a;  NLS_LANGUAGESIMPLIFIED CHINESE  NLS_CHA…

xftp连接linux及xftp下载安装

xftp一、下载二、安装三、Xftp连接linux一、下载 网址&#xff1a;https://www.netsarang.com/zh/xftp/ 1.打开这个网址后&#xff0c;点击下图的下载 2.不要去下载盗版&#xff0c;我们可以下载官网的免费版&#xff0c;点击家庭版的免费授权页面 3.接下来再填写你的姓名和…

xshell与xftp如何实现相互连接

xshell与xftp互联工具操作步骤1.xftp连接xshell2.xshell连接xftphttps://blog.csdn.net/hanhanwanghaha宝藏女孩 欢迎您的关注&#xff01; 欢迎关注微信公众号&#xff1a;宝藏女孩的成长日记 让这个可爱的宝藏女孩在努力的道路上与你一起同行&#xff01; 如有转载&#xff0…

xftp如何显示隐藏的文件详解

https://blog.csdn.net/hanhanwanghaha宝藏女孩 欢迎您的关注&#xff01; 欢迎关注微信公众号&#xff1a;宝藏女孩的成长日记 如有转载&#xff0c;请注明出处&#xff08;如不注明&#xff0c;盗者必究&#xff09; xftp可以帮助在Windows和Linux系统之间传输文件&#xff0…