为什么要使用哈希表
查找和插入是查找表的两项基本操作,对于单纯使用链表,数组,或二叉树实现的查找表来说,这两项操作在时间消耗上仍显得比较昂贵。 以查找为例:在数组实现的查找表中,需要用二分等查找方式进行一系列的比较后,才能找到给定的键值对的位置。而二叉树的实现中也存在着一个向左右子树递归查找的过程。 而现在,我们希望在查找/插入/删除这三项基本操作里, 能不通过比较,而是通过一个哈希函数的映射,直接找到键对应的位置,从而取得时间上的大幅优化, 这就是我们选用哈希表的原因。
相比起哈希表,其他的查找表中并没有特定的“键”和“键的位置”之间的对应关系。所以需要在键的查找上付出较大的开销。而哈希表则通过一个映射函数(哈希函数)建立起了“键”和“键的位置”(即哈希地址)间的对应关系,所以大大减小了这一层开销
哈希表的取舍
所谓选择,皆有取舍。哈希表在查找/插入/删除等基本操作上展现的优越性能,是在它舍弃了有序性操作的基础上实现的。因为哈希表并不维护表的有序性,所以在哈希表中实现有序操作的性能会很糟糕。例如:max(取最大键),min(取最小键), rank(取某个键的排名), select(取给定排名的键),
floor(向下取整) ceiling(向上取整)。 而相对的, 用二叉树等结构实现的查找表中,因为在动态操作(插入/删除)中一直维护着表的有序性,所以这些数据结构中实现的有序操作开销会小很多。
使用哈希表的前提
使用哈希表的前提是: 这个表存储的键是无序的,或者不需要考虑其有序性
哈希函数的构造
哈希函数有许多不同的构造方法,包括:1.直接定址法 2.数字分析法 3.平方取中法 4.折叠法 5. 除留取余法
1.直接定址法
取键或键的某个线性函数值为哈希地址。设 f 为哈希函数,key为输入的键,则f(key) = key或者 f(key) = k*key+b (k,b为常数)
例如,有一个解放后的人口调查表, 键为年份,则可设置哈希函数为: f(key) = key+ (-1948),如下图所示:

1949对应的哈希函数值为1, 1950对应的为2,依次类推
2.数字分析法
如下图所示,有80个记录,每一行为一个记录中的键,假设表长为100,则可取两位十进制数组成哈希地址。

通过观察可以得出,第1,2列对应的数字都是相同的,而第3列和第8列存在大量重复的数字(分别是3和2,7),不能选做哈希地址。而中间4位可以看作是随机的,可以从中任选两位作为哈希地址
3. 平方取中法
取关键字平方后的中间几位为哈希地址,这种方法叫做平方取中法。它弥补了数字分析法的一些缺陷,因为我们有时并不能知道键的全部情况,取其中几位也不一定合适,而一个数平方后的中间几个数和原数的每一位都相关,由此我们就能得到随机性更强的哈希地址取的位数由表长决定。

4.折叠法
将关键字分成位数相同的几部分(最后一位可以不同),然后取叠加和作为哈希地址,这一方法被称为折叠法。当表的键位数很多,而且每一位上数字分布比较均匀的时候, 可以考虑采用这一方法。 折叠法有移位叠加和间位叠加两种方法例如国际标准图书编号0-442-20586-4的哈希地址可以用这两种方法表示为

5.除留余数法
除留余数法是最基础的,最常用的取得哈希函数的方法。选定一个统一的基数, 对所有键取余,从而得到对应的哈希地址。下图中的M就表示这个统一的基数,在实现上,它一般是数组的长度

哈希地址的冲突
一个经常会碰到的问题是; 不同的键经过哈希函数的映射后,得到了一个同样的哈希地址。这种现象叫做冲突(或者碰撞)如下图所示。

解决冲突的方法
冲突并不是一件严重的事情,因为我们可以用一些方式去解决它
解决冲突的方式有

哈希表通过哈希函数实现快速查找、插入和删除操作,牺牲有序性以提高性能。文章介绍了哈希表的取舍、使用前提,以及哈希函数的构造方法,包括直接定址法、数字分析法等,并探讨了冲突解决策略,如拉链法、线性探测法和再哈希法。
最低0.47元/天 解锁文章
365

被折叠的 条评论
为什么被折叠?



