关于散列表的学习笔记

还想看更多文章的朋友可以访问我的个人博客


散列表

散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。

哈希表hashtable(key,value) 的思想其实很简单,就是把Key(任意长度)通过一个固定的算法函数,即所谓的哈希函数转换成一个整型数字(固定长度),然后就将该数字对数组长度进行取余,取余结果就当作数组的下标,将value存储在以该数字为下标的数组空间里。

而当使用哈希表进行查询的时候,就是再次使用哈希函数将key转换为对应的数组下标,并定位到该空间获取value,如此一来,就可以充分利用到数组的定位性能进行数据定位。

散列算法。

根据 key(数据特征) 计算出对应数据的存储位置的算法叫做散列算法(position=hash(key);,又称哈希算法。

散列算法需要满足一下条件:

  • 对输入值进行运算后,应得到固定长度的计算结果;
  • 对于不同的输入值,得到的结构可能相同。
  • 散列函数的输出值尽量接近均匀分布。
  • x的微小变化可以使f(x)发生非常大的变化,即所谓“雪崩效应”(Avalanche effect)。

例如:

f(x) = x \% 16
f(x) = (x^2 + 10) * x
f(x) = (x | 0×0000FFFF) XOR (x >> 16)

除法散列法

公式:

f(x) = x % N(N为常数,不大于数组的长度)

取关键字被某个不大于散列表表长 L 的数 N 除后所得的余数为散列地址。对 N 的选择很重要,一般取"素数"或 L,若 N 选的不好,容易产生同义词

平方散列法

公式:

index = (value * value) >> 28 (右移,除以2^28。记法:左移变大,是乘。右移变小,是除。)

  当无法确定关键字中哪几位分布较均匀时,可以先求出关键字的平方值,然后按需要取平方值的中间几位作为哈希地址。这是因为:平方后中间几位和关键字中每一位都相关,故不同关键字会以较高的概率产生不同的哈希地址。

  如果数值分配比较均匀的话这种方法能得到不错的结果,但我上面画的那个图的各个元素的值算出来的index都是0——非常失败。还有个问题,value如果很大,value * value不会溢出吗?答案是会的,但我们这个乘法不关心溢出,因为我们根本不是为了获取相乘结果,而是为了获取下标。

斐波那契散列法

公式:

index = (value * num) >> 28(此处的num为特定的乘数,其余与平方散列法相同)

  在上述“平方散列法”中,需要以 value 的值作为乘数,有没有可能用特定的数值作为乘数。

  • 对于16位整数而言,这个乘数是40503
  • 对于32位整数而言,这个乘数是2654435769
  • 对于64位整数而言,这个乘数是11400714819323198485.

哈希冲突

哈希重复,也就是两个不同输入通过哈希函数运算所得到的地址值相同的情况。哈希冲突是无法避免的,因此,哈希算法的选择直接决定了哈希冲突发送的概率;同时必须要对哈希冲突进行处理。

开放寻址法

开放寻址法,即把所有的元素都存放在散列表中,也就是每个表项包含动态集合的一个元素(元素可以为NULL)。

  1. 在开放寻址法中,当要插入一个元素时,可以连续地检查散列表的各个项(连续检查是可以通过不同的算法获得偏移位),直到找到一个空槽来放置这个元素为止。
  2. 当查找一个元素时,要检查所有的表项,直到找到所需的元素,或者最终发现元素不在表中。
  3. 在开放寻址法中,对散列表元素的删除操作执行起来比较困难。当我们从槽 i 中删除关键字时,不能仅将此位置元素置空。因为这样做的话,会导致在无法判断此位置是否有元素。应该用个特殊的值表示该元素已经删除。

公式:

Hi=(H(key) + di) % m , [ i = 1, 2,…,k * (k <= m-1) ]

其中H(key)为散列函数,m为散列表长;di为增量序列,可有下列三种取法:

线性探测再散列

公式:di = 1,2,3,…,m-1
  线性再散列法是最简单的处理冲突的方案。插入数据时,当发生冲突,算法会自动从该空间的位置向后遍历哈希表,直到找到下一个空间为空的位置。(这样会导致相同hash的数据重复存放,且会占用其他hash所对应的空间。)查询数据时,算法会先定位到该hash所对应的位置空间上,若是没找到匹配数据则会从该位置继续向后遍历哈希表,直到找到:

  • 对应的数据,返回对应数据。
  • 空的空间,并返回所对应的值为空。
  • 遍历结束也未出现上两种情况,返回未找到,并表示该哈希表是满的。
线性探测法处理冲突的但存在下列缺点:
  • 处理溢出需另编程序。一般可另外设立一个溢出表,专门用来存放上述哈希表中放不下的记录。此溢出表最简单的结构是顺序表,查找方法可用顺序查找。
  • 按上述算法建立起来的哈希表,删除工作非常困难。如果将此元素删除,查找的时会发现空槽,则会认为要找的元素不存在。只能标上已被删除的标记,否则,将会影响以后的查找。
  • 线性探测法很容易产生堆聚现象。所谓堆聚现象,就是存入哈希表的记录在表中连成一片。按照线性探测法处理冲突,如果生成哈希地址的连续序列愈长 ( 即不同关键字值的哈希地址相邻在一起愈长 ) ,则当新的记录加入该表时,与这个序列发生冲突的可能性愈大。因此,哈希地址的较长连续序列比较短连续序列生长得快,这就意味着,一旦出现堆聚 ( 伴随着冲突 ) ,就将引起进一步的堆聚。
二次探测再散列

公式:di = 1^2, -1^2, 2^2, -2^2, 3^2,…,±(k)^2, (k <= m/2)

伪随机探测再散列

公式:di = pseudo-random

  随机探测的基本思想是:将线性探测的步长从常数改为随机数,即令: hash = (hash + RN) % m ,其中 RN 是一个随机数。在实际程序中应预先用随机数发生器产生一个随机序列,将此序列作为依次探测的步长。这样就能使不同的关键字具有不同的探测次序,从而可以避 免或减少堆聚。基于与线性探测法相同的理由,在线性补偿探测法和随机探测法中,删除一个记录后也要打上删除标记。

拉链法

最常见的实现方案如下,可称之为“数组链表法”。数组的特点是:寻址容易,插入和删除困难;而链表的特点是:寻址困难,插入和删除容易。结合两者的特性的数据结构便是寻址,插入删除代价都相对均衡的哈希表。其中大致结构如下:

图1. Entry数组实现哈希表

  从上图的结构中可以看出,左边的连续存储结构为数组,而数组的每个空间都存放一个指向链表头部的指针。需要按元素的特征将数据分配到不同的链表中去。而将元素特征转换为数组下标的方法就是“散列法”。(例如 Java中的 HashTable、HashMap 都是使用“拉链法”实现)。

拉链法的优点:
  • 拉链法处理冲突简单,且无堆积现象,即非同义词决不会发生冲突,因此平均查找长度较短;
  • 由于拉链法中各链表上的结点空间是动态申请的,故它更适合于造表前无法确定表长的情况;
  • 开放定址法为减少冲突,要求装填因子α较小,故当结点规模较大时会浪费很多空间。而拉链法中可取α≥1,且结点较大时,拉链法中增加的指针域可忽略不计,因此节省空间;
  • 在用拉链法构造的散列表中,删除结点的操作易于实现。只要简单地删去链表上相应的结点即可。
拉链法的缺点

指针需要额外的空间,故当结点规模较小时,开放定址法较为节省空间,而若将节省的指针空间用来扩大散列表的规模,可使装填因子变小,这又减少了开放定址法中的冲突,从而提高平均查找速度。




本篇文章是写于 2017.08.07 的学习笔记,那时正是酷暑,稍稍坐一会就汗流浃背。别家空调压缩机排出的热气通过门下的缝隙灌入我的房间,使我的室温甚至高于窗外艳阳下的温度,挥汗写下这篇学习笔记。正因如此,我也知道肯定错误百出,望大家多加指正。

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

推荐阅读更多精彩内容