《数据结构与算法图解》 第7章 散列表

第7章 散列表

大多数编程语言都自带散列表,也叫做散列、映射、散列映射、字典、关联数组。

        A=1
        B=2
        C=3
        D=4
        E=5

由此可得,ACE会转化为135, CAB会转化为312, DAB会转化为412, BAD会转化为214。将字符串转为数字串的过程就是散列,其中用于对照的密码,就是散列函数

一个散列函数需满足以下条件才有效:每次对同一字符串调用该散列函数,返回的都应是同一数字串。如果每次都返回不一样的结果,那就无效。

现在要实现同义词字典的效果:

散列表可以看成是一行能够存储数据的格子,就像数组那样。每个格子都有对应的编号,如下所示。

在这里插入图片描述

现在往散列表里加入我们的第一条同义词。

        thesaurus["bad"]="evil"

散列表变成了下面这样。

        {"bad"=> "evil"}

再看看散列表是如何存储数据的。首先,计算机用散列函数对键进行计算。为了方便演示,这里我们依然使用之前提及的那个乘法函数。

"bad"的散列值为8,于是计算机将"evil"放到第8个格子里。

在这里插入图片描述

收到命令后,计算机就会进行如下两步简单的操作。

(1) 计算这个键的散列值:BAD=2×1×4=8。

(2) 由于结果是8,因此去到第8格并返回其中的值。在本例中,该值为"evil"。这下你应该明白为什么从散列表里读取数据只需要 ==O(1)==了吧,因为其过程所花的时间是恒定的。它总是先计算出键的散列值,然后根据散列值跳到对应的格子去。

处理冲突

不过,散列表也会带来一些麻烦。继续同义词典的例子:把下面这条同义词也加到表里,会发生什么呢?

        thesaurus["dab"]="pat"

首先,计算散列值。

        DAB=4 * 1 * 2=8

然后,将"pat"放进第8个格子。

在这里插入图片描述

往已被占用的格子里放东西,会造成冲突。幸好,我们有解决办法。一种经典的做法就是分离链接。当冲突发生时,我们不是将值放到格子里,而是放到该格子所关联的数组里。

因为要放入"pat"的第8格,已经存在"evil"了,于是我们将第8格的内容换成一个数组。该数组又以子数组构成,每个子数组含两个元素,第一个是被检索的词,后一个是其相应的同义词。

在这里插入图片描述

下面运行一遍"dab"的查找过程,执行:

        thesaurus["dab"]

计算机就会按如下步骤执行。

(1) 计算散列值DAB=4×1×2=8。

(2) 读取第8格,发现其中不是一个单独的值,而是一个数组

(3) 于是线性地在该数组中查找,检查每个子数组的索引0位置,如果碰到要找的词(“dab”),就返回该子数组的索引1的值。

如果数据都刚好存在同一个格子里,那么查找就相当于在数组上进行。因此散列表的最坏情况就是O(N)

为了避免这种情况,散列表的设计应该尽量减少冲突,以便查找都能以==O(1)==完成。

找到平衡

使用散列表时所需要权衡的:既要避免冲突,又要节约空间。

要想解决这个问题,可参考计算机科学家研究出的黄金法则:每增加7个元素,就增加10个格子。

  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值