您的位置:首页 > 理论基础 > 数据结构算法

数据结构与算法 | 排序与查找:什么是散列

2020-04-20 19:44 483 查看

【推荐阅读】微服务还能火多久?>>>

当一组数据项的排列是无序时,我们就用顺序查找;当数据项是有序时,我们可以用二分查找法来降低算法复杂度,从顺序查找法的O(n),降低到二分查找法的O(log n),从而实现更高效的查找。

那么问题来了,我们能否进一步降低查找的算法复杂度呢?

答案是,能。

现在,我们进一步构造一个新的数据结构,能使得查找算法的复杂度降低到O(1),也就是常数级别。实现的这种概念就是散列(hashing)。

散列(hashing)

要使得查找的次数降低到常数级别,先要对数据项所处的位置有更多的先验知识——如果事先能知道要找的数据项应该出现在数据集里的什么位置,就可以直接到那个位置查看数据项是否存在即可。 由数据项的值来确定其存放位置,如何能做到这一点呢?

散列表(hash table,又称哈希表)就是我们的答案。

散列表,是一种数据集,其中数据项的存储方式尤其有利于将来快速的查找定位。

散列表中的每一个存储位置,称为槽(slot),是用来保存数据项的。每个槽都有唯一的名称。

实现从数据项到存储槽名称转换的,称为散列函数(hash function)。

一个散列的示例

下面示例中,散列函数接受数据项作为参数,返回整数值0~10,表示数据项存储的槽号(名称)。 假设我们有这么一些数据项:

54, 26, 93, 17, 77, 31

有一种常用的散列方法“求余数”,将数据项除以散列表的大小,得到的余数作为槽号。

实际上“求余数”方法会以不同形式出现在所有的散列函数里。

因为散列函数返回的槽号必须在散列表大小范围之内,所以一般会对散列表大小求余。

在我们的这个示例中,因为槽号的下标是0~10,一共11个槽,所以散列函数是最简单的求余:

h(item) = item % 11

按照散列函数h(item),为每个数据项计算出存放的位置之后,就可以将数据项存入相应的槽中。如下表所示:

Item Hash Value
54      10
26      4
93      5
17      6
77      0
31      9

可以看到,示例中的6个数据项各自占据了1个槽,也就是6个数据项一共占据了11个槽中的6个。这种槽被数据项占据的比例称为散列表的“负载因子”。本例中的负载因子是6/11。

我们把数据项都保存在散列表中之后,查找就非常简单了。

如果要查找某个数据项是否存在于表中,我们需要使用同一个散列函数,对查找项进行计算,测试下返回的槽号所对应的槽中是否有数据项即可。

如此,我们便实现了复杂度为O(1)的查找算法。

散列表查找方法的劣势

上述例子虽然实现了O(1)复杂度的查找算法,但是,我们注意到,示例比较特殊,6个数据项占据了6个槽,也就是每个槽只有1个独一无二的数据项。

那么问题来了,如果再增加1个数据项,比如44,h(44)=0,就造成了#0槽有两个数据项,7个数据项只占据了6个槽的情况,即有一个槽有2个不同的数据项。这种情况称为冲突(collision)。

关于冲突的情况,我们后面再讨论其解决方案。

本文仅供学习之用,侵删。

在学习Python的道路上肯定会遇见困难,别慌,我这里有一套学习资料,包含40+本电子书,800+个教学视频,涉及Python基础、爬虫、框架、数据分析、机器学习等,不怕你学不会! https://shimo.im/docs/JWCghr8prjCVCxxK/ 《Python学习资料》

关注公众号【Python圈子】,优质文章每日送达。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: