哈希表刷题总结——基于数组、集合、映射的实现

一.什么是哈希表

哈希表(Hash Table)也称为散列表,是一种基于散列函数的从(key)直接访问对应内存空间的数据结构。由名称中的“表”不难推知,哈希表的一大特点就是加快查找的速度。

了解哈希表,首先要认识散列函数。        

散列函数是一种将输入数据映射到固定大小的输出值的函数。它通常用于数据存储和查找中,例如在哈希表中。散列函数的主要目标是将输入数据均匀地分布到输出范围内,以便快速查找和访问数据。

散列函数具有以下属性:

  1. 决定性:给定相同的输入,散列函数应始终返回相同的输出。
  2. 均匀性:散列函数应将输入数据均匀地分布到输出范围内,以减少冲突的可能性。
  3. 高效性:散列函数应具有高效的计算速度,以便在实际应用中能够快速处理大量数据。
  4. 抗碰撞性:散列函数应尽量避免不同的输入映射到相同的输出,以减少冲突的发生。

由散列函数的定义和性质不难发现,利用上了散列函数高效而精确特性的哈希表,在一般的程序设计中常用来以空间复杂度换取更小时间复杂度,同时不影响原数据结构查找和搜索的准确性。

这样的描述可能非常抽象。用大白话来讲,一个数组就能是一张哈希表,这个数组的索引/下标,就是所谓的(key),而下标对应位置存入的(value),就是我们直接访问的“对应内存空间”。

类似于指针,我们生成一张哈希表的时候,就类似于复制了键对应的内存空间里面存着的那个值,并不是真的把那一块内存空间赋给了哈希表里面。而当我们通过哈希表的下标访问这个值的时候,我们得到的值,和我们通过指针去访问对应的那一块内存空间所得到的值,是完全一样的。

例如我们有很多个学生的学号,从1到7,我们把每个学生的名字当成元素存入对应的下标,这个时候下标就和学生的学号对应起来,一张从学号到名字的哈希表就建成了。

上一篇博客中,提到过数组用与模拟的优势,这里就得到了明显的体现。一个数组并没有真正建立从下标到内存空间的映射,它只是在模仿”通过索引访问得到一个值“的这个过程,而c++的STL里的map就是实实在在地做到了这一过程。尽管如此,两者最终得到的结果却是相同的,所以使用起来也不会太过在意其实现这一过程的具体方式。

二.什么时候需要哈希表

一般需要用上哈希表时,多是:

        判断一个元素是否在某个集合里出现过

和它的名字一样,哈希表是一张表,如果能打表,应该没有人会不喜欢。。。毕竟时间复杂度只有O(1),除非你的空间真的很紧缺。。。

三.哈希碰撞

一个key有多个value映射过来的时候,我们称这一现象为哈希碰撞。

解决哈希碰撞的方法主要是拉链法线性探测法。

1.拉链法

拉链法的落脚点在上,也就是在发生碰撞的索引处建立一个短链表,把多个值链接在一起,查找的时候遍历一遍即可,当然了,链表太长就失去打表的意义了。

2.线性探测法

线性探测法落脚点在线性,也就是在碰撞的索引处开辟额外的连续内存空间。

四.如何实现哈希表

使用数组来模拟生成一张哈希表就不多赘述了。

数组模拟当然容易,但一张散列表的特点就是散,用数组这样的连续线性存储结构是很浪费空间的。为了替代数组,c++的STL中的map和set都提供了便捷可行的模板。

set有三类,分别为set,multiset 和unordered_set,和名字一样,分别是有序不重复集合,有序可重复集合,无序不重复集合。

map同理,也是上述三种:map,multimap和unordered_map。

其中前两种的底层实现都是红黑树(R-B tree),而后一种的实现是哈希表,并且前两种的时间复杂度都是O(logn),而后一种只有O(1),是更优的选择。

五.哈希表的应用

采用数组实现的哈希表:力扣242,383

采用set实现的哈希表:力扣349,202

采用map实现的哈希表:力扣1,15,18,454(分别是二数,三数,四数之和问题)

具体做题的时候,打哈希表也是很有技巧的一件事:

力扣454题,求四个不同数组中四数之和。若是直接采用四个for循环来遍历,肯定是会tle的,而直接一个个遍历打出来哈希表,时间复杂度也高达O(n^3),较好的解决方式是利用分治的思想,把四个遍历分成2+2个遍历,前两个遍历打出A+B的表,后两个遍历进行查找-(C+D)的操作,这样时间复杂度就缩小到了O(n^2),很大但是能过。

但是,并不是所有问题都能用哈希表完美解决的,15和18题就是如此。这两道题多是采用双指针+去重+剪枝来完成,这是解决这类问题的一个通法,理解了原理,实现起来也要注意一些很小的细节。

  • 27
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Tomokochandesu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值