哈希表2s总结

news2024/11/20 23:29:56

3.哈希表

哈希表非常常用,字典一般会用来保存处理过后的输入输出信息,集合也可以用来去重,这部分是重点,但是还是那句话,这种题目是不会或者说很少考原题的,主要还是学习知识,所以题目看一下答案理解一下知识就过了,不要纠结会不会出原题。

哈希表理论基础

哈希表中关键码就是数组的索引下标,然后通过下标直接访问数组中的元素,如下图所示:

image-20240404112713269

⼀般哈希表都是⽤来快速判断⼀个元素是否出现集合⾥。
例如要查询⼀个名字是否在这所学校⾥。
要枚举的话时间复杂度是O(n),但如果使⽤哈希表的话, 只需要O(1)就可以做到。
我们只需要初始化把这所学校⾥学⽣的名字都存在哈希表⾥,在查询的时候通过索引直接就可以知道这位同学在不
在这所学校⾥了。
将学⽣姓名映射到哈希表上就涉及到了hash function ,也就是哈希函数。

哈希函数

哈希函数,把学⽣的姓名直接映射为哈希表上的索引,然后就可以通过查询索引下标快速知道这位同学是否在这所
学校⾥了。
哈希函数如下图所示,通过hashCode把名字转化为数值,⼀般hashcode是通过特定编码⽅式,可以将其他数据格
式转化为不同的数值,这样就把学⽣名字映射为哈希表上的索引数字了。

image-20240404113107435

存在两种情形

1.如果hashCode得到的数值⼤于 哈希表的⼤⼩了,也就是⼤于tableSize了

此时为了保证映射出来的索引数值都落在哈希表上,我们会在再次对数值做⼀个取模的操作,就要我们就保证了学
⽣姓名⼀定可以映射到哈希表上了。

2.哈希表我们刚刚说过,就是⼀个数组。如果学⽣的数量⼤于哈希表的⼤⼩怎么办,此时就算哈希函数计算的再均匀,也避免不了会有⼏位学⽣的名字同时映射到哈希表 同⼀个索引下标的位置。这会引起哈希碰撞

image-20240404113330027

⼀般哈希碰撞有两种解决⽅法, 拉链法和线性探测法。

拉链法
刚刚⼩李和⼩王在索引1的位置发⽣了冲突,发⽣冲突的元素都被存储在链表中。 这样我们就可以通过索引找到⼩
李和⼩王了(数据规模是dataSize, 哈希表的⼤⼩为tableSize)

image-20240404113617940

其实拉链法就是要选择适当的哈希表的⼤⼩,这样既不会因为数组空值⽽浪费⼤量内存,也不会因为链表太⻓⽽在
查找上浪费太多时间。

线性探测法
使⽤线性探测法,⼀定要保证tableSize⼤于dataSize。 我们需要依靠哈希表中的空位来解决碰撞问题。
例如冲突的位置,放了⼩李,那么就向下找⼀个空位放置⼩王的信息。所以要求tableSize⼀定要⼤于dataSize ,
要不然哈希表上就没有空置的位置来存放 冲突的数据了。如图所示

image-20240404113719050

常⻅的三种哈希结构

当我们想使⽤哈希法来解决问题的时候,我们⼀般会选择如下三种数据结构。
数组
set (集合)
map(映射)
这⾥数组就没啥可说的了,我们来看⼀下set。
在C++中,set 和 map 分别提供以下三种数据结构,其底层实现以及优劣如下表所示:

image-20240404113900727

std::unordered_set底层实现为哈希表,std::set 和std::multiset 的底层实现是红⿊树,红⿊树是⼀种平衡⼆叉搜
索树,所以key值是有序的,但key不可以修改,改动key值会导致整棵树的错乱,所以只能删除和增加。

image-20240404113936295

std::unordered_map 底层实现为哈希表,std::map 和std::multimap 的底层实现是红⿊树。同理,std::map 和
std::multimap 的key也是有序的(这个问题也经常作为⾯试题,考察对语⾔容器底层的理解)。

当我们要使⽤集合来解决哈希问题的时候,优先使⽤unordered_set,因为它的查询和增删效率是最优的,如果需
要集合是有序的,那么就⽤set,如果要求不仅有序还要有重复数据的话,那么就⽤multiset。

那么再来看⼀下map ,在map 是⼀个key value 的数据结构,map中,对key是有限制,对value没有限制的,因
为key的存储⽅式使⽤红⿊树实现的。

虽然std::set、std::multiset 的底层实现是红⿊树,不是哈希表,std::set、std::multiset 使⽤红⿊树来索引和存
储,不过给我们的使⽤⽅式,还是哈希法的使⽤⽅式,即key和value。所以使⽤这些数据结构来解决映射问题的⽅
法,我们依然称之为哈希法。 map也是⼀样的道理。
这⾥在说⼀下,⼀些C++的经典书籍上 例如STL源码剖析,说到了hash_set hash_map,这个与unordered_set,
unordered_map⼜有什么关系呢?

实际上功能都是⼀样⼀样的, 但是unordered_set在C++11的时候被引⼊标准库了,⽽hash_set并没有

image-20240404114458583

总结

总结⼀下,当我们遇到了要快速判断⼀个元素是否出现集合⾥的时候,就要考虑哈希法。
但是哈希法也是牺牲了空间换取了时间,因为我们要使⽤额外的数组,set或者是map来存放数据,才能实现快速
的查找。
如果在做⾯试题⽬的时候遇到需要判断⼀个元素是否出现过的场景也应该第⼀时间想到哈希法!

哈希表套路总结

数组作为哈希表

⼀些应⽤场景就是为数组量身定做的。
在242.有效的字⺟异位词中,我们提到了数组就是简单的哈希表,但是数组的⼤⼩是受限的!
这道题⽬包含⼩写字⺟,那么使⽤数组来做哈希最合适不过。
在383.赎⾦信中同样要求只有⼩写字⺟,那么就给我们浓浓的暗示,⽤数组!
本题和242.有效的字⺟异位词很像,242.有效的字⺟异位词是求 字符串a 和 字符串b 是否可以相互组成,在383.赎
⾦信中是求字符串a能否组成字符串b,⽽不⽤管字符串b 能不能组成字符串a。
⼀些同学可能想,⽤数组⼲啥,都⽤map不就完事了。
上⾯两道题⽬⽤map确实可以,但使⽤map的空间消耗要⽐数组⼤⼀些,因为map要维护红⿊树或者符号表,⽽
且还要做哈希函数的运算。所以数组更加简单直接有效!

set作为哈希表

在349. 两个数组的交集中我们给出了什么时候⽤数组就不⾏了,需要⽤set。
这道题⽬没有限制数值的⼤⼩,就⽆法使⽤数组来做哈希表了。
主要因为如下两点:
数组的⼤⼩是有限的,受到系统栈空间(不是数据结构的栈)的限制。
如果数组空间够⼤,但哈希值⽐较少、特别分散、跨度⾮常⼤,使⽤数组就造成空间的极⼤浪费。
所以此时⼀样的做映射的话,就可以使⽤set了。
关于set,C++ 给提供了如下三种可⽤的数据结构:(详情请看关于哈希表,你该了解这些!)
std::setstd::multiset
std::unordered_set
std::set和std::multiset底层实现都是红⿊树,std::unordered_set的底层实现是哈希, 使⽤unordered_set 读写效
率是最⾼的,本题并不需要对数据进⾏排序,⽽且还不要让数据重复,所以选择unordered_set。
在202.快乐数中,我们再次使⽤了unordered_set来判断⼀个数是否重复出现过。

map作为哈希表

在1.两数之和中map正式登场。
来说⼀说:使⽤数组和set来做哈希法的局限。
数组的⼤⼩是受限制的,⽽且如果元素很少,⽽哈希值太⼤会造成内存空间的浪费。
set是⼀个集合,⾥⾯放的元素只能是⼀个key,⽽两数之和这道题⽬,不仅要判断y是否存在⽽且还要记录y的
下标位置,因为要返回x 和 y的下标。所以set 也不能⽤。
map是⼀种 <key, value> 的结构,本题可以⽤key保存数值,⽤value在保存数值所在的下标。所以使⽤map最
为合适。
C++提供如下三种map::(详情请看关于哈希表,你该了解这些!)
std::map
std::multimap
std::unordered_map
std::unordered_map 底层实现为哈希,std::map 和std::multimap 的底层实现是红⿊树。
同理,std::map 和std::multimap 的key也是有序的(这个问题也经常作为⾯试题,考察对语⾔容器底层的理
解),1.两数之和中并不需要key有序,选择std::unordered_map 效率更⾼!
在454.四数相加中我们提到了其实需要哈希的地⽅都能找到map的身影。
本题咋眼⼀看好像和18. 四数之和,15.三数之和差不多,其实差很多!
关键差别是本题为四个独⽴的数组,只要找到A[i] + B[j] + C[k] + D[l] = 0就可以,不⽤考虑重复问题,⽽18. 四数
之和,15.三数之和是⼀个数组(集合)⾥找到和为0的组合,可就难很多了!
⽤哈希法解决了两数之和,很多同学会感觉⽤哈希法也可以解决三数之和,四数之和。
其实是可以解决,但是⾮常麻烦,需要去重导致代码效率很低。
在15.三数之和中我给出了哈希法和双指针两个解法,⼤家就可以体会到,使⽤哈希法还是⽐较麻烦的。
所以18. 四数之和,15.三数之和都推荐使⽤双指针法!

双指针法一定要排序,一旦排序后原来数组的索引就被改变

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1570127.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JS详解-手写Promise!!!

前言&#xff1a; 针对js的深入理解&#xff0c;作者学习并撰写以下文章&#xff0c;由于理解认知有限难免存在偏差&#xff0c;请大家指正&#xff01;所有定义来自mdn。 Promise介绍&#xff1a; 对象表示异步操作最终的完成&#xff08;或失败&#xff09;以及其结果值. 描…

基于SpringBoot+Vue光影视频平台(源码+部署说明+演示视频+源码介绍)

您好&#xff0c;我是码农飞哥&#xff08;wei158556&#xff09;&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。&#x1f4aa;&#x1f3fb; 1. Python基础专栏&#xff0c;基础知识一网打尽&#xff0c;9.9元买不了吃亏&#xff0c;买不了上当。 Python从入门到精通…

ChernoCPP 2

视频链接&#xff1a;【62】【Cherno C】【中字】C的线程_哔哩哔哩_bilibili 参考文章&#xff1a;TheChernoCppTutorial_the cherno-CSDN博客 Cherno的C教学视频笔记&#xff08;已完结&#xff09; - 知乎 (zhihu.com) C 的线程 #include<iostream> #include<th…

SV学习笔记(六)

覆盖率类型 写在前面 覆盖率是 衡量设计验证完备性 的一个通用词。随着测试逐步覆盖各种合理的场景&#xff0c;仿真过程会慢慢勾画出你的设计情况。覆盖率工具会 在仿真过程中收集信息 &#xff0c;然后进行后续处理并且得到覆盖率报告。通过这个报告找出覆盖之外的盲区&…

设计模式——原型模式05

原型模式核心复制&#xff0c;每次new出来的对象完全不一样&#xff0c;实现对象之间的隔离。 学习前最好先掌握jAVA值传递和深浅拷贝 设计模式&#xff0c;一定要敲代码理解 浅拷贝 克隆出对象&#xff0c;其中两者的引用类型属性是同一个对象。 对象信息 /*** author ggb…

C++:逻辑运算符-非与或(19)

!非!a如果a为假&#xff0c;那么当前他就是真&#xff0c;如果a是真&#xff0c;那么他直接就是假&&与a&&ba与b都为真&#xff0c;那么就是真&#xff0c;如果两个里面有一个为假那么就是假||或a||ba或b有一个为真&#xff0c;那么就是真 非&#xff08;!&…

怎样把学浪购买的课程下载下来

如何把学浪已购买的课程下载下来?这里就教大家一个方法,利用一个工具轻轻松松把视频下载下来 这个工具我打包成压缩包了,有需要的自己取一下 链接&#xff1a;https://pan.baidu.com/s/1y7vcqILToULrYApxfEzj_Q?pwdkqvj 提取码&#xff1a;kqvj --来自百度网盘超级会员V1…

基于springboot+vue+Mysql的在线考试系统

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

redis集合Set

set是一种无序集合。它和列表的区别在于列表中的元素都是可以重复的&#xff0c;而set中的元素是不能重复的。而且set中的元素&#xff0c;并不像列表那样是具有顺序的。 SADD是添加一个元素。course是集合。 SMEMBERS SISMEMBER判断Redis在不在集合course里 SREM是用来删除Re…

Jupyter Notebook安装使用(一)

1. 简介 Jupyter Notebook 是一个非常强大的工具&#xff0c;它允许用户创建和共享包含实时代码、方程式、可视化和叙事文本的文档。这种工具特别适合数据清理和转换、数值模拟、统计建模、数据可视化、机器学习等多种应用领域。 2. 安装Jupyter Notebook 2.1. 使用 Anaconda…

校招说明书

3400字的详细说明&#xff0c;介绍了程序员类岗位校招的整体时间节点和招聘流程。还对一些常见的问题进行讨论&#xff0c;例如内推、offer和三方、实习等。 第一章介绍基本的术语&#xff0c;第二章介绍整个校招的重要流程及时间点&#xff0c;然后第三章介绍每次招聘要经过的…

golang 和java对比的优劣势

Golang&#xff08;或称Go&#xff09;和Java都是非常流行的编程语言&#xff0c;被广泛应用于各种领域的软件开发。尽管它们都是高级编程语言&#xff0c;但它们具有许多不同的特性和适用场景。本文将重点比较Golang和Java&#xff0c;探讨它们的优势和劣势。 性能方面&#…

JSP

文章目录 JSP1. 快速入门2. page 指令3. 三种常用脚本声明脚本表达式脚本代码脚本 4. 注释5. 内置对象6. 域对象7. 请求转发标签8. EL 表达式快速入门EL运算操作EL的11个隐含对象四个特定域变量 9. JSTL快速入门<c:set /><c:if />\<c:choose> \<c:when>…

【微服务】------核心组件架构选型

1.微服务简介 微服务架构&#xff08;Microservice Architecture&#xff09;是一种架构概念&#xff0c;旨在通过将功能分解到各个离散的服务中以实现对解决方案的解耦&#xff0c;从而降低系统的耦合性&#xff0c;并提供更加灵活的服务支持。 2.微服务技术选型 区域内容…

爬虫学习第一天

爬虫-1 爬虫学习第一天1、什么是爬虫2、爬虫的工作原理3、爬虫核心4、爬虫的合法性5、爬虫框架6、爬虫的挑战7、难点8、反爬手段8.1、Robots协议8.2、检查 User-Agent8.3、ip限制8.4、SESSION访问限制8.5、验证码8.6、数据动态加载8.7、数据加密-使用加密算法 9、用python学习爬…

汽车疲劳测试试验平台技术要求(北重厂家)

汽车疲劳测试试验平台技术要求通常包括以下几个方面&#xff1a; 车辆加载能力&#xff1a;测试平台需要具备足够的承载能力&#xff0c;能够同时测试多种车型和不同重量的车辆。 动力系统&#xff1a;测试平台需要具备稳定可靠的动力系统&#xff0c;能够提供足够的力和速度来…

C++ 指针与数组

指针与数组名都是地址&#xff0c;可以混合使用访问数组元素。 用指针访问数组&#xff0c;计算数组元素之和。 总结 如图所示&#xff0c;获取数组起始地址的方法有两种&#xff0c; 其一为数组名&#xff0c; 其二为通过数组的首元素地址。指针变量p是通过数组名获得指向…

通用开发技能系列:SQL基础学习

云原生学习路线导航页&#xff08;持续更新中&#xff09; 本文是 通用开发技能系列 文章&#xff0c;主要对编程通用技能 SQL基础 进行学习 1.数据库简介 1.1.数据库中的一些名称 DataBase&#xff1a;数据库 程序员只负责怎么维护存取数据&#xff0c;不管数据库是什么 DBA…

Deep Unsupervised Learning using Nonequilibrium Thermodynamics

就直接从算法部分开始了&#xff1a; 2 算法 我们的目标是定义一个前向&#xff08;或者推理&#xff09;扩散过程&#xff0c;这个过程能够转换任意的复杂数据分部到一个简单、tractable、分布&#xff0c;并且学习有限时间扩散过程的反转 从而 定义我们的生成模型分布。我们…

“人性化设计”技术概要

本文是由《埃森哲技术愿景 2024&#xff1a;“人性化设计”技术将通过提高生产力和创造力来重塑行业并重新定义领导者》这个文章来翻译解读的。原文地址如下&#xff0c;大家可以自行下载&#xff1a; 下载地址 其实看到这篇文章的时候&#xff0c;联想到这些年机器人的市场发展…