目录
本文参考:大话数据结构——程杰。之前看了一些博客,觉得内容零零散散,还是看书较容易理解。以下内容大部分来自书里。
1.散列表概述
举个最简单的例子,我们要存储0 2 4 6 8 10这6个数到数组里,直接将它们除以2,对应的值就为存储数组的下标,即2存在arr[1],10存在arr[5]。我们要查找数据时只需要同样将数据除以2,不需要和任何数据比较,直接找出这个数据所在位置。比如我们要查找10,10/2=5即为其所在位置,因此直接查找arr[5]的数据。从而得到O(1)时间复杂度的查找。这里的除以而,就可以看作一个最简单的哈希函数。
1.1散列表查找定义
散列技术是在记录的存储位置和它的关键字之间建立一个确定的对应关系f,使得每个关键字key对应一个存储位置f (key)。查找时,根据这个确定的对应关系找到给定值key的映射f (key),若查找集合中存在这个记录,则必定在f (key)的位置上。
这里我们把这种对应关系f称为散列函数,又称为哈希(Hash)函数。按这个思想,采用散列技术将记录存储在一块连续的存储空间中,这块连续存储空间称为散列表或哈希表(Hashtable)。那么关键字对应的记录存储位置我们称为散列地址。
1.2散列表查找步骤
(1)在存储时,通过散列函数计算记录的散列地址,并按此散列地址存储该记录。
(2)当查找记录时,我们通过同样的散列函数计算记录的散列地址,按此散列地址访问该记录。说起来很简单,在哪存的,上哪去找,由于存取用的是同一个散列函数,因此结果当然也是相同的。
所以说,散列技术既是一种存储方法,也是一种查找方法。然而它与线性表、 树、图等结构不同的是,前面几种结构,数据元素之间都存在某种逻辑关系,可以用连线图示表示岀来,而散列技术的记录之间不存在什么逻辑关系,它只与关键字有关联。因此,散列主要是面向查找的存储结构。
散列技术最适合的求解问题是査找与给定值相等的记录。对于查找来说,简化了比较过程,效率就会大大提高。但万事有利就有弊,散列技术不具备很多常规数据结构的能力。
比如那种同样的关键字,它能对应很多记录的情况,却不适合用散列技术。一个 班级几十个学生,他们的性别有男有女,你用关键字“男”去查找,对应的有许多学 生的记录,这显然是不合适的。只有如用班级学生的学号或者身份证号来散列存储, 此时一个号码唯一对应一个学生才比较合适。
同样散列表也不适合范围查找,比如查找一个班级18〜22岁的同学,在散列表 中没法进行。想获得表中记录的排序也不可能,像最大值、最小值等结果也都无法从 散列表中计算出来。
2.散列函数的构造方法
不管做什么事要达到最优都不容易,既要付出尽可能的少,又要得到最大化的多。那么什么才算是好的散列函数呢?这里我们有两个原则可以参考。
1.计算简单
你说设计一个算法可以保证所有的关键字都不会产生冲突,但是这个算法需要很复杂的计算,会耗费很多时间,这对于需要频繁地查找来说,就会大大降低查找的效率了。因此散列函数的计算时间不应该超过其他查找技术与关键字比较的时间。
2.散列地址分布均匀
我们刚才也提到冲突带来的问题,最好的办法就是尽量让散列地址均匀地分布在 存储空间中,这样可以保证存储空间的有效利用,并减少为处理冲突而耗费的时间。
接下来我们就要介绍几种常用的散列函数构造方法。
2.1直接定址法
如果我们现在要对0〜100岁的人口数字统计表,如下表所示,那么我们对 年龄这个关键字就可以直接用年龄的数字作为地址。此时f (key) =key。
如果我们现在要统计的是80后出生年份的人口数,如下表所示,那么我们 对出生年份这个关键字可以用年份减去1980来作为地址。此时f (key) =key-1980。
也就是说,我们可以取关键字的某个线性函数值为散列地址,即
f ( key ) =a x key+b ( a、b 为常数)
这样的散列函数优点就是简单、均匀,也不会产生冲突,但问题是这需要事先知 道关键字的分布情况,适合查找表较小且连续的情况。由于这样的限制,在现实应用中,此方法虽然简单,但却并不常用。
2.2数字分析法
如果我们的关键字是位数较多的数字,比如我们的11位手机号 “130xxxxl234”,其中前三位是接入号,一般对应不同运营商公司的子品牌,如130 是联通如意通、136是移动神州行、153是电信等;中间四位是HLR识别号,表示用 户号的归属地;后四位才是真正的用户号