算法导论瞎抄一点-散列表HashTable的Recap

前言:

任何一个应用都要用到至少一种数据结构

比如说我们的程序用到数据库,底层多半是优化后的b-tree.

我们做一个最简单的Android程序,甚至不用到数据库,比方说用到SharedPreferences,那就是xml表单。

甚至我们自己发明一个表单也是可以的,只要写好对应的解析器和编码器,然后给文件取一个没被占用的后缀名即可。

这样的做法用到文件系统,文件系统也有底层的数据结构,可以说VFS也可以具体到磁盘,比如大家熟知的NTFS啥的。

它至少要支持Insert, Search和Delete操作。

比方说数据挖掘作用的non-sql data cube是通常没有update,这和数据集合的功能有关,不清楚的可以看看数据挖掘相关书籍,或者看看我的文章(这个系列没看书看起来确实有点...呵呵)

今天我们来看一下散列表(Hashtable),他用到了(Hash)的处理方式,使得算法平均时间是 \omicron (n) ,虽然理论最坏的情况是 \theta (n) .

1.直接寻址表(direct-address table)

实际上这就是一个数组的高大上别名,其中数组的每一个存元素的位置被称为槽(slot)

它适用于全域U较小的情况

提到数组的具体数据结构,我们毫无疑问要提到一个概念叫做卫星数据(satellite data)

卫星数据,可以形象的思考地球和卫星的关系,逻辑上它围绕地球,距离上它并不靠近地球。

因此卫星数据在磁盘中是地址任意而被当前数据结构使用的数据,连接当前数据结构和卫星数据的方法是记录卫星数据的地址(指针)

如果了解C/C++的朋友肯定觉得这个概念小儿科了,不再赘述。

//TODO insert graph 11-1

2.散列表(hash table)

直接寻址技术的缺点很明显,如果全域U较大,即数据规模较大,要存储大小为|U|的一张表不太实际(连续内存分配问题),也会造成很大内存浪费。

存在字典中的关键字集合(keyword set)比全域U小许多时,散列表所需的存储空间要比直接寻址表少很多。

直接寻址方式下,具有关键字k的元素被放在槽k中(见上图11-1)。而在散列方式下,该元素存放在槽h(k)中。即利用散列函数h(k)计算出槽的位置。

散列函数是全域U向散列表T的映射。

映射这个东西就是高一数学,我们知道,函数是单射的,也就是说每个关键字k只会映射到1个槽。

但同样我们学习过高阶函数,y=x^2 这种程度,同一个因变量y就会有多个自变量x对应了。

更不要提三角函数/脉冲函数等情况了...

因此我们需要考虑同一个槽被多个关键字k映射到的情况,即冲突问题。

//TODO insert 11-2

解决冲突问题也有办法:

使h产生的结果更加没有规律性可言,当然,h(k)是固定的函数。

散列的意义就是“杂乱拼凑”,这毫无疑问会减少冲突,使数据尽可能均等的存在于槽(slot)中。

但不要忘了,U是远大于T的,即入射集合远大于出射集合。

我们一定是会遇到冲突的,因此还要考虑解决冲突的办法:

第1种方法.链接法(chaining)

把所有槽的所有元素都放在一个双向链表中。

如果不存在这样的元素,槽中存NIL.

这很好理解,就是数组套链表。

//TODO insert graph 11-3

性能分析:

依照我们的定义,每个slot中的元素个数可以大于1,等于1和小于1.

所以最坏的情况,是所有U映射而出的元素都到了1个slot,即这个slot是一个元素个数为#U的双向链表,设这个长度为n。

这时,查询时间为\theta (n),再加上散列函数的时间,他就和用一个链表来链接所有元素差不多了(建立链表所花的时间n,对于如果是n->C的函数,也可以视为总体时间n)

链接法的性能毫无疑问取决于链表的长度均匀程度,假设每个k被等可能的散列到所有槽中,我们成这个假设为简单均匀散列(Simple Uniform Hashing)

对于j=0,1,...,m-1,列表T[j]的长度用n_{j}表示,于是有:

                                    n=n_{0}+n_{1}+...+n_{m-1}... (1)

并且nj的期望值E[n_{j}]=\alpha =n/m,其中m为slot的个数,n为所有链表的总长度。

定理 在简单均匀散列的假设下,对于用连接尅发解决冲突的散列表,一次查找的平均时间为\Theta (1+\alpha )

不成功即查找到链表末尾,成功最坏的情况也是查询到链表末尾。

另一种解决冲突的方法叫开放寻址法,之后会的第4节讲到。

3.散列函数

好的散列函数的特点:独立均匀分布

将关键字转换为自然数:如果关键字k不是自然数,就要转化为自然数,以适用于函数,这种转换即字符表,ASCII码表是主流操作。

3.1除法散列法

h(k) = k mode m 可以将U中的所有k通过余数的不同映射到m个槽中。

3.2乘法散列法

h(k) = \lfloor m(kA\space mod \space 1)\rfloor

即kA的小数部分乘以m向下取整。

其中A是常数,0<A<1。

这样做的好处是,m的选择不是特别关键,一般选择它为2的幂,这样便于2进制运算。

例如某计算机的字长有\omega 位,就取A为形如s/2^{w}的一个分数,当然A的范围还是(0, 1)

尽管如此,Knuth认为黄金分割比A=(\sqrt{5}-1 )/2是最好的取值

3.3全域散列法

如果一个恶意的对手来针对某个特定的散列函数选择要散列的关键字,他就可以把n个关键字散列到同一个槽中,使得散列的平均检索时间最坏。

如果需要应付这种攻击,我们只有建立一个散列函数组,并选定随机函数,对散列函数进行随机抽取。

4.开放寻址法(open addressing)

在开放寻址法中,所有的元素都存放在散列表中。也就是说,每个表象或包含动态集合的一个元素,或包含NIL.

在查找一个元素时,要系统的检查所有的表项。这和链接法的最大区别是,开放寻址法中,没有任何元素存放在散列表外,也不会有链表。

因此在开放寻址法中,散列表可能会被填满,以至于不能插入新的元素。该方法导致的一个结果是装载因子\alpha 绝对不会超过1.(即每个slot最多装一个)

当然,也可以将链表放入槽中,但开放寻址法的好处就在于它不用指针,而是计算出要存取的槽序列。

这就省了指针那点内存,使得同样的内存空间可以容纳更多的槽,客观上减少了冲突出现的概率。

为了使用开放寻址法插入一个元素,需要连续的检查散列表,这个操作称之为探查(probe).

To be Continued.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,525评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,203评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,862评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,728评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,743评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,590评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,330评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,244评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,693评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,885评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,001评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,723评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,343评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,919评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,042评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,191评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,955评论 2 355

推荐阅读更多精彩内容

  • 说明:该系列博客整理自《算法导论(原书第二版)》,但更偏重于实用,所以晦涩偏理论的内容未整理,请见谅。另外本人能力...
    黑夜0411阅读 1,467评论 0 2
  • 散列表(hash table)是实现字典操作的一种有效数据结构,尽管最坏情况下,散列表中的查找一个元素的时间与链表...
    Mrsunup阅读 1,350评论 0 2
  • 散列表是支持 INSERT 、DELETE 和 SEARCH 的字典操作,其是对普通数组概念的推广,因为可以对数组...
    点融黑帮阅读 867评论 0 11
  • 有一句库尔德谚语:"The belly is like a garden -- anything can grow...
    philewar阅读 221评论 0 1
  • 今天下午,老家的一个弟弟来公司补打合同,晚上有5个客户来公司参加活动,结束后,赶回家接儿子跆拳道学习,回来给他洗澡...
    卓彤的美好时光阅读 150评论 0 0