第九章·词典

散列:原理

散列是一种赖以高效组织数据并实现相关算法的重要思想。

这样的思想背后的原理却非常直观、简单。



上图是IBM公司和联想公司的服务电话号码,能够看到这样的号码是由数字和字母共同组成的。字母的含义通常是公司名相关。而这些字母通过键盘输入后。仍然是数字。

循值訪问

对于訪问数据的方式,大致有例如以下分类

訪问方式 含义 举例
call-by-rank 循秩訪问 vector向量
call-by-position 循位置訪问 list列表
call-by-key 循关键码訪问 BST平衡二叉树等
call-by-value 循值訪问 Hashing散列

散列的实现原理



1. 以电话号码为例,关键码空间为存储全部电话号码的数组,比方从000000-999999。显然假设直接採用循位置訪问的方式,所占用空间将非常巨大而且实际利用率非常低。

2. 为了节省空间,散列採用一种“哈希表(散列表)”的方式存储关键码数据,称为散列地址空间。

3. 从关键码空间找到哈希表相应地址的过程称为“寻址”,这样的方式用一个“哈希函数”来实现。如图中所看到的,由关键码空间的key通过hash(key)找到哈希表中的&entry,再由此找到相应的词条entry。

4. 仅仅要哈希函数设计得当。能够使得转换过程的时间复杂度控制在常数范围。

5. 哈希表的空间M应该远小于可能空间R,同一时候稍大于实际空间N。

  • 哈希函数的设计

    以电话号码为例,将电话号码对哈希表的长度取余,得到的余数即为哈希表的相应位置。

    1. 能够看到。此哈希函数的时间复杂度为O(1)
    2. 散列表的空间效率取决于实际空间N与哈希表空间M的比值,这一比值称为Load factor(装填因子)。简记为λ。
  • 解决冲突

    因为哈希表是将一个相对较大的空间映射为一个较小的空间。那么必定存在不同的关键码在哈希表中占用同一个位置的情况。怎样解决这样的冲突呢?

    1. 精心设计散列表和散列函数。以尽可能减少冲突的概率
    2. 制定可行的预案,排解产生的冲突

散列:散列函数

散列函数设计标准

  • 除余法:在除余法中,散列表长(也就是模)应该怎么选?



1. 假设将模取做2^k,这样对于数字计算是最高效的(与运算就可以),可是这样的方式的均匀性非常差。存在一个特定集合的数据经过除余后映射到散列表中的某一个特定单元。

2. 一种较为良好的策略是将模设定为素数。

此时。数据对散列表的覆盖能够达到最充分。在散列表中的分布也将达到最均匀。

3. 在訪问散列表数据时,通常是依照一个固定的“步长”S来訪问的,比方电话号码的步长为1.整个散列表长度为M。要达到均匀性的目的。就是要依照S的间隔訪问散列表的元素,能够遍历整个散列表空间。依照数论的知识,能够得到当且仅当(S,M)的最大公因子为1.因此M仅仅能为素数。

1977年。著名古生物学家史蒂芬·杰·古尔德(StephenJayGould)提出了一个新假说。觉得周期蝉这样做是为了避开自己的天敌。

他指出,非常多蝉的天敌也有自己的生命周期。假如周期蝉的生命周期不是质数,那么就会有非常多机会和天敌的周期重叠。

比方12年蝉就会和周期为2、3、4、6年的天敌重叠,被吃的可能性就要大非常多。

——蝉:生命周期为素数的进化论优势

  • MAD法

    1. 由mad法的计算公式可知,b能够视为偏移量。这样能够解决不动点的缺陷。

      a相当于步长,亦即相邻元素的间隔。从而解决第二个缺陷。

    2. 散列的一个优势在于。其散列函数是能够灵活定制的。

      有些场合并不须要非常强的均匀性,比方几何计算。将高维空间的点压缩到低维空间时。仍然保持点之间的邻近位置。

      这样的方式称为Locality-Sensitive Hashing。password学也採用了特定的散列规则。比方消息摘要算法。

  • 平方取中法



    注:取居中的数位的目的是使得原关键码每个数位都对散列地址构成接近的影响。

  • 折叠法

  • 伪随机数

    伪随机数的生成原理与散列原理非常类似,都是讲一个范围内的数随机、均匀、确定转换为还有一个范围的数。

    因此散列函数能够參考伪随机数的生成原理来设计。

    但注意:不同平台。或者同一平台不同版本号的伪随机数算法可能是不同的。因此假设直接套用伪随机数算法,将使得散列算法的移植性非常差。

  • 多项式法

    原始关键码未必都是整数,也能够是字符串等其它结构。因此须要先对其进行转换。变成散列码HashCode。字符串型关键码的转换方式为多项式法:



    注:将字符先转换为相应的整数,将这些数分别视作为一个n次多项式的n个系数。计算出这个多项式的详细数值。并将其作为散列地址。

  • 字符串折叠转换法

    除了多项式法之外,也有一种类似前面折叠法的转换方法。即简单讲字符代表的整数相加得到散列地址。

    这样的方法的缺点是将得到大量同样的散列地址。比方:



    由此能够看出:Harry Potter和VoldeMort是同一个人(HashCode转换函数必须非常复杂!

    )

散列:解决冲突

多槽位法

解决冲突的方式:在每个散列空间的bucket(桶)中,设置多个“槽位”。

假设在同一个桶中存在冲突。能够放置在不同的槽位中。



可是这样的方法的缺陷是:无法预知一个桶须要设置多少个槽位。

假设设置过多,则空间利用率将大幅减少。假设设置过少,则可能仍然存在冲突。

独立链法

使用列表:每个桶存放一个指针,冲突的词条组织成列表。



缺陷:因为不同的节点插入、删除顺序随机。因此对于不论什么一个列表而言。其节点空间可能不是连续分布。因此无法通过有效地缓存加速查找过程。当散列表的规模非常大,以至于要借助IO时,这一缺陷就更加突出了。

Question:这节中说道,独立链是动态生成的。也就是说有多少个实际关键码。就会生成多少个节点。那么,何不在刚開始就把全部实际关键码进行存储呢?

Answer:这是因为散列表的设计,是为了平衡时间和空间。假设将实际关键码进行顺序存储。那么每次查找的时间复杂度为O(n),所需的空间最少。假设把关键码作为数组索引直接查找,那么时间复杂度仅为O(1),可是须要可能超出有效空间几个数量级。

哈希表是一个在时间和空间上做出权衡的经典样例。假设没有内存限制,那么能够直接将键作为数组的索引。

那么全部的查找时间复杂度为O(1)。假设没有时间限制,那么我们能够使用无序数组并进行顺序查找,这样仅仅须要非常少的内存。哈希表使用了适度的时间和空间来在这两个极端之间找到了平衡。仅仅须要调整哈希函数算法就可以在时间和空间上做出取舍。

——浅谈算法和数据结构: 十一 哈希表

开放定址

  1. 封闭定址:在散列表中每个桶相应的列表中,其能够存放的元素仅仅能是在这个桶中发生冲突的词条。
  2. 开放定址:放弃使用列表。全部的散列以及冲突排解都在一个独立、连续的封闭空间内,每个冲突词条都可能存在不论什么一个桶中。词条优先存放在其本来应该放置的桶中(即发生冲突的桶),假设本应该放置的桶已经存放了词条,则依照一定的次序查找下一个空桶进行存放(若已到达表尾,则回到表首继续查找直到不存在空桶为止)。这样的查找链称为该词条的“试探链”或者“查找链”。
  3. 相应的。通过查找链查找冲突词条时。有两种结果:成功匹配;或者遇到空桶。查找失败。
  4. 应用开放定址策略的散列称为“闭散列”。
  5. 开放定址法Flash演示

.

  • 线性试探

    线性试探是开放定址一种简单地实现方式。

  • 懒惰删除

    之前说到。通过查找链查找时。假设遇到空桶,则查找失败。

    那么删除词条的方式不应该是将桶置空。而是应该使用一个删除标记。

  • 平方试探

    为了解决线性试探可能导致冲突过于密集的情况。採用将间距扩大的方法。能够有效避免。平方试探即是将关键码的偏移量进行平方处理。



    平方试探的缺点是:可能存在空桶,可是试探链找不到。例如以下图所看到的



    这条定理的证明步骤例如以下,没看懂,就不解释了:(



    依据这条定理能够得出查找链在M/2取上整的各项互异。然而,有没有能够使得前M项都互异的方法呢?答案是有的。

  • 双向平方试探



    双向平方试探也有一个缺点:正向子查找链和逆向子查找链可能存在除了起点0之外的共同元素。那么,他们的查找项之和无法全部覆盖表长。怎么解决呢?表长不仅设为素数,还设为特殊的素数:4K+3。使得查找链全部覆盖表长且互异。



    证明此结论须要用到双平方定理(费马提出):



    详细证明步骤例如以下(呵呵,记住就好了):

通排序、计数排序

通排序、计数排序的性能不全然取决于待排序元素的规模n。也取决于待排序元素的取值范围M。这样的算法的渐进时间复杂度为O(n+M)。这样的算法适用于M和n同阶或低阶的情况。



桶排序、计数排序的原理是利用散列,以字符排序为例:



1. 建立长度为26(字符总数)的散列表。其值包含count(计数)和accum(累计)。count表示当前字母出现的次数,accum表示当前字符在输入序列中的次序。accum是前面全部字符的count的总和。

2. count的计算能够在o(n)的时间内完毕:遍历待排序数组,在散列表相应位置累加计数就可以。

3. accum的计算能够在o(M)时间(即常数时间)内完毕,仅仅要将count累加就可以。

4. 通过count值和accum值,就可以确定当前字符在排序后的位置范围。

5. 这里仅仅是说明桶排序对散列表的应用。

很多其它桶排序、计数排序的原理參考

第九章·词典的更多相关文章

  1. 精通Web Analytics 2.0 (11) 第九章: 新兴分析—社交,移动和视频

    精通Web Analytics 2.0 : 用户中心科学与在线统计艺术 第九章: 新兴分析-社交,移动和视频 网络在过去几年中发生了不可思议的发展变化:从单向对话到双向对话的转变; 由视频,Ajax和 ...

  2. 第九章:四大组件之Broadcast Receiver

    第九章:四大组件之Broadcast Receiver   一.广播的功能和特征 广播的生命周期很短,经过调用对象-->实现onReceive-->结束,整个过程就结束了.从实现的复杂度和 ...

  3. [Effective Java]第九章 异常

    声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...

  4. 第九章 C语言在嵌入式中的应用

    上章回顾 编码的规范和程序版式 版权管理和申明 头文件结构和作用 程序命名 程序注释和代码布局规范 assert断言函数的应用 与0或NULL值的比较 内存的分配和释放细节,避免内存泄露 常量特性 g ...

  5. Laxcus大数据管理系统2.0(11)- 第九章 容错

    第九章 容错 在当前,由于集群庞大的组织体系和复杂性,以及用户普遍要求低成本硬件,使得集群在运行过程中发生的错误概率,远远高于单一且性能稳定的小型机服务器,并且集群在运行过程中几乎是不允许停止的,这就 ...

  6. Android群英传》读书笔记 (4) 第八章 Activity和Activity调用栈分析 + 第九章 系统信息与安全机制 + 第十章 性能优化

    第八章 Activity和Activity调用栈分析 1.Activity生命周期理解生命周期就是两张图:第一张图是回字型的生命周期图第二张图是金字塔型的生命周期图 注意点(1)从stopped状态重 ...

  7. [转]Windows Shell 编程 第九章 【来源:http://blog.csdn.net/wangqiulin123456/article/details/7987969】

    第九章 图标与Windows任务条 如果问一个非程序人员Windows最好的特色是什么,得到的答案应该是系统最有吸引力的图标.无论是Windows98现在支持的通用串行总线(USB)还是WDM(看上去 ...

  8. perl5 第九章 关联数组/哈希表

    第九章 关联数组/哈希表 by flamephoenix 一.数组变量的限制二.定义三.访问关联数组的元素四.增加元素五.创建关联数组六.从数组变量复制到关联数组七.元素的增删八.列出数组的索引和值九 ...

  9. Knockout应用开发指南 第九章:高级应用举例

    原文:Knockout应用开发指南 第九章:高级应用举例 1   Contacts editor 这个例子和微软为演示jQuery Data Linking Proposal例子提供的例子一样的提供的 ...

随机推荐

  1. Kinect2.0获取关节姿态(Joint Orientation)

    Bones Hierarchy 骨骼层次结构从SpineBase作为根节点开始,一直延伸到肢体末端(头.指尖.脚): 层级结构如下图所示: 通过IBody::GetJointOrientations函 ...

  2. mysql计算时间差值,单位分钟数

    TIMESTAMPDIFF(MINUTE, 开始时间, 结束时间) as 时间差(单位:分钟数) TIMESTAMPDIFF(interval,datetime_expr1,datetime_expr ...

  3. python之模块datetime 常见操作

    # -*- coding: utf-8 -*- #python 27 #xiaodeng #python之模块datetime #http://blog.sina.com.cn/s/blog_6c37 ...

  4. 《web与移动开发》征文活动

    活动简介: 首次征文活动有PHPChina主办,是面向全国所有的IT界活动,希望大家大家以PHPChina论坛为载体,积极的分享技术.希望技术的分享为大家的工作猪跑,也希望大家早日成为技术行业的中流砥 ...

  5. 开源分布式搜索平台ELK(Elasticsearch+Logstash+Kibana)入门学习资源索引

    from:  http://www.w3c.com.cn/%E5%BC%80%E6%BA%90%E5%88%86%E5%B8%83%E5%BC%8F%E6%90%9C%E7%B4%A2%E5%B9%B ...

  6. 苹果后门、微软垄断与Linux缺位

    7月21日,法国学者J.Zdziarski指出苹果系统存在人为的预先设置的"系统后门".危害用户的信息安全. 次日,苹果承认了系统存在诊断"后门". 7月28日 ...

  7. 【php正则】php正则匹配UTF-8格式的中文汉字 和 【,】【,】【。】等符号

    1.php正则匹配UTF-8格式的中文汉字 和 [,][,][.]等符号 if (preg_match_all("/([\x{4e00}-\x{9fa5}]+((,)?)+((,)?)+(( ...

  8. ajax post方式下载后台传来的文件

    参考:http://stackoverflow.com/questions/16086162/handle-file-download-from-ajax-post $.ajax({ type: &q ...

  9. Tensorflow get_variable和Varialbe的区别

    import tensorflow as tf """ tf.get_variable()和Variable有很多不同点 * 它们对重名操作的处理不同 * 它们受name ...

  10. linux平台下server运维问题分析与定位

    结合我工作中碰到的运维问题,总结一下Linux下server常见的运维问题以及定位方式.这里的server主要指自主开发的逻辑server,web srv因为通常采用通用的架构所以问题比较少. 逻辑s ...