数据结构与算法笔记(九)哈希表

哈希表是一种利用函数映射实现快速查找的数据结构,通过哈希函数将关键字直接转化为存储地址。哈希冲突是其主要问题,可以通过开放定址法、链地址法等方法解决。哈希表在高级语言中常被黑盒化处理,提供高效插入、删除和查找操作。举例说明了哈希表在字符串计数场景的应用。

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

哈希表

哈希表名字源于 Hash,也可以叫作散列表。哈希表是一种特殊的数据结构,它与数组、链表以及树等我们之前学过的数据结构相比,有很明显的区别。

哈希表的核心思想

哈希表的设计采用了函数映射的思想,将记录的存储位置与记录的关键字关联起来。这样的设计方式,能够快速定位到想要查找的记录,而且不需要与表中存在的记录的关键字比较后再来进行查找

简单理解为    地址  = f(关键字)    

通过构建哈希表,直接构建关键字到地址的映射关系,查询可以降低时间复杂度,仅为O(1),之前的其他数据类型查询的时间复杂度为O(n)

哈希函数的致命问题——哈希冲突

假如Hash函数为每个字的开头大写字母的ASCII码之和

address (张一) = ASCII (Z) + ASCII (Y) = 90 + 89 = 179;

address (张二) = ASCII (Z) + ASCII (E) = 90 + 69 = 159;

address (张三) = ASCII (Z) + ASCII (S) = 90 + 83 = 173;

address (张四) = ASCII (Z) + ASCII (S) = 90 + 83 = 173;

f(张三)和f(张四)都是173,这个现象就叫哈希冲突

从本质上来看,哈希冲突只能尽量减少,不能完全避免。因为只要输入的数据量够多,分布够广,就可能发生冲突。

因此,哈希表需要设计合理的哈希函数,并且对冲突有一套处理机制。

设计哈希表

常用设计方法:

  • 直接定址法

哈希函数为关键字到地址的线性函数。如,H (key) = a*key + b      (a、b为常数)

  • 数字分析法

假设关键字集合中的每个关键字 key 都是由 s 位数字组成(k1,k2,…,Ks),并从中提取分布均匀的若干位组成哈希地址

  • 平方取中法

如果关键字的每一位都有某些数字重复出现,并且频率很高,我们就可以先求关键字的平方值,通过平方扩大差异,然后取中间几位作为最终存储地址

  • 折叠发

如果关键字的位数很多,可以将关键字分割为几个等长的部分,取它们的叠加和的值(舍去进位)作为哈希地址

  • 除留余数法

预先设置一个数 p,然后对关键字进行取余运算。即地址为 key mod p

哈希冲突的解决方法

  • 开放定址法

即当一个关键字和另一个关键字发生冲突时,使用某种探测技术在哈希表中形成一个探测序列,然后沿着这个探测序列依次查找下去。当碰到一个空的单元时,则插入其中。

常用的探测方法是线性探测法。 比如有一组关键字 {12,13,25,23},采用的哈希函数为 key mod 11。当插入 12,13,25 时可以直接插入,地址分别为 1、2、3。而当插入 23 时,哈希地址为 23 mod 11 = 1。然而,地址 1 已经被占用,因此沿着地址 1 依次往下探测,直到探测到地址 4,发现为空,则将 23 插入其中。

  • 链地址法

将哈希地址相同的记录存储在一张线性链表中。

例如,有一组关键字 {12,13,25,23,38,84,6,91,34},采用的哈希函数为 key mod 11。

 

哈希表优势:提供快速的插入、删除、查找操作。无论多少数据,插入和删除值都接近常量的时间,在查找方面,哈希表速度比树还快。

哈希表不足:没有顺序概念,不能以固定方式遍历元素(从大到小等)。在数据处理顺序敏感的问题,哈希表不是好方法,且哈希表的key是不允许重复的,在重复性高的数据中,哈希表也不是好方法

哈希表的基本操作

在很多高级语言中,哈希函数、哈希冲突都已经在底层完成了黑盒化处理,是不需要开发者自己设计的。也就是说,哈希表完成了关键字到地址的映射,可以在常数级时间复杂度内通过关键字查找到数据。

哈希表查找的细节过程是:对于给定的 key,通过哈希函数计算哈希地址 H (key)。

如果哈希地址对应的值为空,则查找不成功。

反之,则查找成功。

虽然哈希表查找的细节过程还比较麻烦,但因为一些高级语言的黑盒化处理,开发者并不需要实际去开发底层代码,只要调用相关的函数就可以了。

例:

1.将关键字序列 {7, 8, 30, 11, 18, 9, 14} 存储到哈希表中。哈希函数为: H (key) = (key * 3) % 7,处理冲突采用线性探测法。

H (7) = (7 * 3) % 7 = 0

H (8) = (8 * 3) % 7 = 3

H (30) = 6

H (11) = 5

H (18) = 5

H (9) = 6

H (14) = 0

Index0123456789
Key7,14  8 11,1830,9   

按照关键字序列以此向哈希表中填入,发生冲突过按照线性探测探测到的第一个空位置填入

最终结果为

Index0123456789
Key714 8 1130189 

查找流程

找7:H(7)=0,根据哈希表在0的位置,匹配,查找完成

找18:H(18)=5,根据哈希表在5的位置,得到结果11,不匹配,然后挪一位,在6的位置得到30,不匹配,再往后挪,在7的位置的的带18,匹配,查找完成。 

2.假设有一个在线系统,可以实时接收用户提交的字符串型关键字,并实时返回给用户累积至今这个关键字被提交的次数。

例:用户输入"abc",系统返回 1。用户再输入"jk",系统返回 1。用户再输入"xyz",系统返回 1。用户再输入"abc",系统返回 2。用户再输入"abc",系统返回 3。

采用哈希表,(哈希表新增、查找常数级时间复杂度O(1))。预先定义好哈希表,输入字符串判断是哈希表中否存在记录

  • 如果存在,对应的value加1
  • 如果不存在,增加到哈希表中,value赋值1,打印value
if (d.containsKey(key_str) {
    d.put(key_str, d.get(key_str) + 1);
}
else{
    d.put(key_str, 1);
}
System.out.println(d.get(key_str));

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值