哈希表的诞生

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

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

为什么要使用哈希表

查找和插入是查找表的两项基本操作,对于单纯使用链表,数组,或二叉树实现的查找表来说,这两项操作在时间消耗上仍显得比较昂贵。 以查找为例:在数组实现的查找表中,需要用二分等查找方式进行一系列的比较后,才能找到给定的键值对的位置。而二叉树的实现中也存在着一个向左右子树递归查找的过程。 而现在,我们希望在查找/插入/删除这三项基本操作里, 能不通过比较,而是通过一个哈希函数的映射,直接找到键对应的位置,从而取得时间上的大幅优化, 这就是我们选用哈希表的原因。

相比起哈希表,其他的查找表中并没有特定的“键”和“键的位置”之间的对应关系。所以需要在键的查找上付出较大的开销。而哈希表则通过一个映射函数(哈希函数)建立起了“键”和“键的位置”(即哈希地址)间的对应关系,所以大大减小了这一层开销

哈希表的取舍

所谓选择,皆有取舍。哈希表在查找/插入/删除等基本操作上展现的优越性能,是在它舍弃了有序性操作的基础上实现的。因为哈希表并不维护表的有序性,所以在哈希表中实现有序操作的性能会很糟糕。例如: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就表示这个统一的基数,在实现上,它一般是数组的长度

这也是我们接下来实现哈希表时采用的哈希函数方法。

哈希地址的冲突

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

解决冲突的方法

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

解决冲突的方式有

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值