数据结构与算法 | 排序与查找:什么是散列
当一组数据项的排列是无序时,我们就用顺序查找;当数据项是有序时,我们可以用二分查找法来降低算法复杂度,从顺序查找法的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圈子】,优质文章每日送达。
- 数据结构——算法之(005)(输入一个已经按升序排序过的数组和一个数字, 在数组中查找两个数,使得它们的和正好是输入的那个数字)
- [课程复习] 数据结构之线性表、树、图、查找、排序经典算法复习
- 数据结构中排序和查找的算法
- 【数据结构与算法】第四章 散列(解决查找问题)
- 数据结构与算法---直接插入排序
- 数据结构与算法——散列表类的C++实现(探测散列表)
- 数据结构与算法之三 深入学习排序
- 数据结构之二分法查找、快速排序思想与实现
- #数据结构与算法学习笔记#剑指Offer1:二维数组中的查找(JAVA)
- 浅析数据结构与算法3--合并排序
- 数据结构基础算法整理归纳:选择排序(四)
- 【数据结构与算法】直接插入排序
- 各种排序和数据结构算法收藏
- 【数据结构与算法】选择排序
- 【数据结构与算法】第五章 优先队列(堆)排序
- Java数据结构与算法之【二分查找】
- 数据结构及简单算法的总结----之【排序】
- 数据结构基础8:散列查找(哈希查找)
- 数据结构——二叉查找(排序)树
- 【数据结构与算法】【查找】哈希表的代码实现