网站开发报价和开发周期/seo外包服务
文章目录
- 什么是哈希
- 问题引入
- 哈希函数
- 直接定址法
- 除留余数法 (常用、重点)
- 哈希冲突
- 哈希冲突的解决方法
- 闭散列
- 开散列
- unordered_map && unordered_set 封装实现
- 哈希的应用
- 位图
- 布隆过滤器
- 哈希经典面试题
- 哈希切分
- 位图应用
- 布隆过滤器
什么是哈希
在上一篇博客map和set我们详细的介绍了以红黑树为底层的数据结构,本篇博客论述的哈希这种数据结构实际上是unordered_map
和unordered_set
的数据结构的底层。
哈希实际上是一种映射关系,map
和set
实际上在搜索的时候对树进行遍历,理论上的时间复杂度是O(log2N),但是哈希是可以不经过任何比较,一次直接从表中得到要搜索的元素。
如果构造一种存储结构,通过某种函数(hashFunc)使元素的存储位置与它的关键码之间能够建立
一一映射的关系,那么在查找时通过该函数可以很快找到该元素。
问题引入
先来一道OJ387. 字符串中的第一个唯一字符
解决这个问题有一个常见的解法:开一个26个空间的int数组,每一个位置映射26个字母表中一个字母,该位置储存的数为该位置映射字母出现的个数。只要遍历一遍字符串即可得到答案。这里就用了哈希的思想。
注意:
- 哈希函数(HashFunc): 这里26个英文字母的映射方法,可以是不同的,例如:可以a映射数组下表为0,b映射下表为2以此类推…,也可以z映射数组下表为0,y映射下表为2以此类推…。这就是哈希函数(函数实际上是一种线性映射关系!),哈希函数是可以自定义的。
- 散列表(HashTable):这里开辟的空间为26的int数组就是散列表,与哈希函数计算出来的存储位置对应。
哈希函数
引起哈希冲突的一个原因可能是:哈希函数设计不够合理。
哈希函数设计原则:
- 哈希函数的定义域必须包括需要存储的全部关键码,而如果散列表允许有m个地址时,其值
域必须在0到m-1之间 - 哈希函数计算出来的地址能均匀分布在整个空间中
- 哈希函数应该比较简单
面对哈希冲突有两种常见的方法:
直接定址法
直接定址法就是上面OJ题的思路是一种一对一的映射关系
取关键字的某个线性函数为散列地址:Hash(Key)= A*Key + B
- 优点:简单、均匀
- 缺点:需要事先知道关键字的分布情况
- 使用场景:适合查找比较小且连续的情况
除留余数法 (常用、重点)
设散列表中允许的地址数为m,取一个不大于m,但最接近或者等于m的质数p作为除数,
按照哈希函数:Hash(key) = key% p(p<=m),将关键码转换成哈希地址
注意:
- 这里的Key与我们在KV模型中的key略有不同, key由于需要取模所以必须是一个正整数——这同时也决定了key是存在范围(一般定义成size_t类型,也就是key只有4294967295种不同情况)
- 如果你想映射一个字符串,理论上不同字符串种类要远远大于不同Key值种类的范围,如果一定让一个字符串对应一个Key,就一定有一些字符串会具有相同的Key值。
哈希冲突
那么问题就出现了,如果不同的值(x)进入哈希函数计算出了相同的值(y)(类比:y=x^2
一个y对应两个x),这时就出现了哈希冲突
把具有不同关键码而具有相同哈希地址的数据元素称为“同义词”。
造成哈希冲突有两个原因(以除留余数法为例):
- 哈希表开的空间太小,导致不同的key映射到了相同数组下表,解决方法也非常简单——数组增容就完事了
- 当你插入值的范围大于Key值的范围时,不管你空间开多大,都会造成哈希冲突,因为不同插入的值会对应相同的Key,这个解决方法是更换哈希函数
哈希冲突的解决方法
闭散列
闭散列:
也叫开放定址法,当发生哈希冲突时,如果哈希表未被装满,说明在哈希表中必然还有空位置,那么可以把key存放到冲突位置中的“下一个” 空位置中去。那如何寻找下一个空位置?
-
线性探测:从发生冲突的位置开始,依次向后探测,直到寻找到下一个空位置为止
对于每一个节点设置三种状态:占用(FULL),空(EMPTY),删除(DELETE)
插入就是修改节点的值和节点的状态为(FULL)
删除 则是直接修改节点的状态为(DELETE),实际上是一种伪删除
散列表的载荷因子:就是插入元素的个数/哈希表的大小,一般在插入的过程中要控制载荷因子在一定的范围内,否则需要扩容来人为降低载荷因子 -
二次探测
线性探测的缺陷是产生冲突的数据堆积在一块,这与其找下一个空位置有关系,因为找空位
置的方式就是挨着往后逐个去找,因此二次探测为了避免该问题,找下一个空位置的方法
key值加上{1,-1,4,-4,9,-9…},如果key加上值的位置已经被占用了,就一直往后加,知道找到空位为止。
总结
闭散列空间利用效率很低
开散列
开散列法又叫链地址法(开链法),首先对关键码集合用散列函数计算散列地址,具有相同地
址的关键码归于同一子集合,每一个子集合称为一个桶,各个桶中的元素通过一个单链表链
接起来,各链表的头结点存储在哈希表中。
这样发送冲突之后就不会影响下一位
插入
插入操作就是先算出插入元素在开散列所在的位置,然后采用链表头插
删除
插入操作就是先算出插入元素在开散列所在的位置,然后删除方法同链表删除元素相同
扩容
先开辟一个新的指针数组,然后遍历原来的开散列,将每一个元素依次按上面的插入逻辑插入新的散列之中
开散列实现代码
unordered_map && unordered_set 封装实现
这里提供unordered_map的封装的代码,unordered_map底层用的是开散列,封装的方式与map是一样的,具体参考博客map和set 的封装
哈希的应用
位图
先来一道腾讯的面试题:
给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在 这40亿个数中。【腾讯】
Solution
- 直接遍历——时间复杂度
O(N)
- 排序之后,二分查找——时间复杂度
O(log2N)
- 位图
位图就是每一个元素都是bit的一维数组,位图的映射是直接定址法,位图仗着自己单个空间小,所以开辟一个很大的数组表示一个很大的范围。但是缺点也很明显:数组元素是bit只能表示两种状态。至于表示多种状态一种常用的方法开辟若干个位图,这样k值映射的位置就对应了多个bit位
布隆过滤器
前面说过一个问题,就是哈希函数是由Key模上散列的大小得出最后的存储位置,但是由于Key必须是正整数所以限制了Key的不同的个数最多只有42亿。但是遇到插入的是字符串这种不同个数是无穷的值时很容易就发生冲突了(如上图所示)。
于是我们做出以下改变,使插入的值不在映射一个位置,而是映射多个位置。相比映射了一个位置添加了多个保险
假设插入顺序是{"hello","world","repeat","bye"}
-
插入
插入的逻辑非常简单,将插入值映射的所有位的值全部置1 -
检查是否存在
- 不存在——只要检查值所对应的位中存在一个位没有被置1,则说明该值未被插入,也就不存在。这个结果是100%确定的。
- 存在——检查值所有对应的位如果全为1 ,则说明该值存在。但是这个结果准确吗?
存在的结果是不准确的,例如上面案例在插入"hello"
和"world"
之后,如果检查"bye"
是否存在的时候,你就会发现bye
所有映射的位全部都置成了1 ,但是显然"bye"
并未被插入。
布隆过滤器应用场景
我们在注册账号的时候输入昵称的时候,需要快速检测该昵称是否被注册过。可以设置一个布隆过滤器在数据库的上层,输入昵称之后不用先到数据库中查询,而是先在布隆过滤器中查询,如果查出来没有,那么一定数据库中不存在该昵称。如果查出来存在,则在去数据库里面查找一下到底是否存在。
代码实现
布隆过滤器的底层是一个位图
实现中的问题
- 如何确定位图应该开多大?
实际上是有公式的:
k为哈希函数个数,m为布隆过滤器底层位图的大小,n为插入元素的个数。因为k是在实现过程中是确定的,所以m和n就成了一个正比例函数
代码
哈希经典面试题
哈希切分
给一个超过100G大小的log file, log中存着IP地址, 设计算法找到出现次数最多的IP地址?
思路:
假设内存为4GB,100G的ip地址不可能都加载到内存中,这里采用一个变式 的哈希桶,但是这个哈希桶挂的不是链表,而是文件
100G中的所有ip都通过HashFunc函数分配到相应的桶之中,保持每一个文件(桶)的大小在1GB左右,分配完之后将每一个文件都加载到内存中用map来统计个数。这是大体思路。
思路漏洞
万一通过哈希函数分配过程中还没分配完某个文件就已经超过了1GB,该如何处理?
分为两种情况:- 冲突的ip多,但都是不同的ip,map是无法计数的——解决方法:更换哈希函数将该文件切分成若干子文件,重复上述思路
- 冲突的ip多,但都是相同的ip,map是可以统计的 ,切分完之后分段放进内存用map统计次数
给两个文件,分别有100亿个query,我们只有1G内存,如何找到两个文件交集?分别给出 精确算法和近似算法
这道题和上一题的思路一致,我们可以对两个文件分别做哈希切分(但是要使用同样的哈希函数),这样相同query才会分到同一个文件夹里面。然后我们在对两个小文件分别加载到内存里面使用set先去重再找交集
位图应用
-
给定100亿个整数,设计算法找到只出现一次的整数?
使用位图,但是位图只能表示该数存在还是不存在,并不能很好的标识出现的次数。所以这里使用两个位图来解决问题,两个位图,每个位对应两个bit位也就能表示四种状态。问题便得到了很好的解决。
思考
100亿个整数开辟位图是不是要100亿个bit位?——
注意bit位的数量是由数据的范围决定的而不是数据的个数,这也是哈希的本质!整数最多也就42亿个,多出来的都是重复的! -
给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集?
使用位图先给一个文件所有整数标记,注意只需注意文件里面数是否存在,出现几次并不在处理范围内。然后遍历第二个文件并用改为图检查,即可找出交集
注意
我们开辟一个42亿的位图~0.5G,生下来0.5G内存用来重复读取文件 -
位图应用变形:1个文件有100亿个int,1G内存,设计算法找到出现次数不超过2次的所有整 数
与第一题思路一模一样
布隆过滤器
如何扩展BloomFilter使得它支持删除元素的操作
现有的布隆过滤器是不支持删除操作的,因为删除某一个元素意味着要清空所有他在位图中的对应位,这可能影响别的元素(因为一个位可能被多个元素共用)。解决方法也很简单:对每个位设置一个计数器,删除某一个元素只需要该元素对应所有位的计数器减1即可。但是缺点也十分明显,会消耗很大的空间,本来位图的优势就是每一个位只占一个bit,现在好了每个位变成一个计数器,空间开销会变得巨大!
相关文章:

哈希表
文章目录什么是哈希问题引入哈希函数直接定址法除留余数法 (常用、重点)哈希冲突哈希冲突的解决方法闭散列开散列unordered_map && unordered_set 封装实现哈希的应用位图布隆过滤器哈希经典面试题哈希切分位图应用布隆过滤器什么是哈希 在上一…...

基于Halcon的MLP(多层感知神经网络)分类器分类操作实例
一、介绍 人工神经网络(Artificial Neural Network,ANN)简称神经网络(Neural Network,NN)或类神经网络,是一种模仿生物神经网络的结构和功能的数学模型或计算模型,用于对函数进行估计或近似。 MLP神经网络是一种基于神经网络、动态的分类器。MLP分类器使用神经…...

VR全景博物馆,打造7*24小时的线上参访体验
导语:博物馆作为人们了解历史、文化和艺术的重要场所,现在可以通过VR全景技术来进行展览,让参观者身临其境地感受历史文化的魅力。本文将介绍博物馆VR全景的特点、优势,以及如何使用VR全景技术来丰富博物馆的展览和教育活动。什么…...

Go 数据类型
基础数据类型 类型长度(字节)默认值说明bool1falsebyte10uint8,取值范围[0,255]rune40Unicode Code Point,int32int,uint4或者8032位或64位操作系统int8,uint810-128~127,0-255int16,uint1620-32768~32767,…...

Mybatis-Plus学透?一篇足够(持续更新中)
01、Mybatis-Plus入门 一、简介 MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。如果你想对自己的项目进行技术升级,不妨尝试将mybatis换成Mybati…...

船用燃料油市场调研报告-主要企业、市场规模、份额及发展趋势
船用燃料油市场报告主要研究:市场规模: 产能、产量、销售、产值、价格、成本、利润等行业分析:原材料、市场应用、产品种类、市场需求、市场供给,下游市场分析、供应链分析等竞争分析:主要企业情况、市场份额、并购、扩…...

python趣味编程-奥赛罗游戏
在上一期我们用Python实现了一个高速公路汽车游戏的游戏,这一期我们继续使用Python实现一个简单的奥赛罗游戏,让我们开始今天的旅程吧~ 在Python中使用Turtle实现的奥赛罗游戏 在Python中使用Turtle的简单奥赛罗游戏 是一个以 Python 为程序设计语言的项…...

经典卷积模型回顾13—ResNetXt实现图像分类(matlab)
ResNetXt是ResNet的变种,在ResNet基础上引入了"split-transform-merge"的思想,旨在进一步提升模型的性能和准确率。ResNetXt模型的核心思想是通过对输入进行分组,然后对每个分组进行不同的变换,最后再将变换后的结果合并…...

Spring学习——Maven进阶
分模块开发与设计 创建模块 书写模块代码 通过maven指令安装模块到本地仓库(install指令) 在pom.xml中导入坐标执行maven的install命令将模块安装到本地maven仓库 团队内部开发可以发布模块功能到团队内部可共享的仓库中(私服) 依赖管理 依赖指当前项目运行所需…...

第23篇:基础知识-Java Switch Case
switch case 语句判断一个变量与一系列值中某个值是否相等,每个值称为一个分支。 switch case 语句语法格式如下: switch(expression){ case value : //语句 break; //可选 case value : //语句 break; //可选 //你可以有任意数量的…...

Go 实现多态和 参数的动态个数及动态类型
引子 go语言作为静态(编译期类型检测)强类型(手写代码进行类型转换)语言, 要想实现 动态语言的鸭子类型的调用方法,做到 一个入参是不同类型,还是有些麻烦的; 需求 希望写代码时像python一样的鸭子类型,不用管参数类型,都可以调用同一个方法;希望 入参像python一样 能够在 个…...

vue 指令
Vue 提供了很多指令,如:v-model, v-show,v-if等等,有利于应付开发时出现的各种情况。Vue 也提供了自定义指令,有利于开发者将某些通用性功能封装成一个指令,进行全局或局部注册。如:复制指令&am…...

APP违法违规收集使用个人信息合规评流程和范围
近期,工信部通报2023年第1批《侵害用户权益行为的APP通报》(总第27批),共通报46款APP(SDK),这些被责令限期整改的APP(SDK),涉及的问题主要包括3个方面&#x…...

【力扣2379】 得到 K 个黑块的最少涂色次数(c++100%)
给你一个长度为 n 下标从 0 开始的字符串 blocks ,blocks[i] 要么是 W 要么是 B ,表示第 i 块的颜色。字符 W 和 B 分别表示白色和黑色。给你一个整数 k ,表示想要 连续 黑色块的数目。每一次操作中,你可以选择一个白色块将它 涂成…...

[2.2.2]进程调度的时机、方式、切换与过程
文章目录第二章 进程管理进程调度的时机、方式、切换与过程(一)进程调度的时机(二)进程调度的方式(三)进程的切换与过程小结第二章 进程管理 进程调度的时机、方式、切换与过程 时机 什么时候需要进程调度…...

第24篇:Java包装类知识深度分析
目录 1、包装类背景 2、包装类的优点 3、包装类与基本类型关系 4、代码示例...

常见问题整理1
目录 偏差和方差 欠拟合underfitting 过拟合overfitting 梯度消失和梯度爆炸 归一化 偏差和方差 偏差:算法期望预测和真实预测之间的偏差程度。反应的是模型本身的拟合能力。 方差:度量了同等大小的训练集的变动导致学习性能的变化,刻画…...

体验Linux 块设备驱动实验(模拟块)
目录 一、块设备 二、块设备驱动框架 1、块设备的注册和注销 2、gendisk 结构体 3、block_device_operations 结构体 4、块设备 I/O 请求过程 ①、请求队列 request_queue ②、bio 结构 三、编写驱动之请求队列 1、修改makefile 2、基本的驱动框架编辑 3、添加头文…...

一文搞懂Linux时区设置、自定义时区文件
概念介绍 常说的 Linux 系统时钟有两个 一个是硬件时钟(RTC),即BIOS时间,一般保存的是 GMT0 时间,没时区、夏令时的概念 一个是当地时钟(LTC),即我们日常经常看到的时间࿰…...

Java实例实验项目大全源码企业通讯打印系统计划酒店图书学生管理进销存商城门户网站五子棋
wx供重浩:创享日记 对话框发送:java实例 获取完整源码源文件视频讲解文档资料等 文章目录1、企业通讯2、快递打印系统3、开发计划管理系统4、酒店管理系统5、图书馆管理系统6、学生成绩管理系统7、进销存管理系统8、神奇Book——图书商城9、企业门户网站…...

基于nvidia xavier智能车辆自动驾驶域控制器设计与实现-百度Apollo架构(二)
智能车辆操作系统 智能车辆操作系统是智能车辆系统的重要组成部分。现代汽车软件组件通常首 先由不同的供应商开发,然后在有限的资源下由制造商进行集成[42]。智能车辆操作 系统需要采用模块化和分层化设计思想来兼容传感器、分布式通信和自动驾驶通用 框架等模块&a…...

考研408 王道计算机考研 (初试/复试) 网课笔记总结
计算机初试、复试笔记总结(导航栏)📝 一、初试 408 408 - 1. 数据结构与算法 数据结构与算法 笔记导航🚥🚥🚥 🥬 第一章 绪论(无)🥕 第二章 线性表🥪 第三章 栈和队列&…...

[Java·算法·中等]LeetCode34. 在排序数组中查找元素的第一个和最后一个位置
每天一题,防止痴呆题目示例分析思路1题解1👉️ 力扣原文 题目 给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。 如果数组中不存在目标值 target,返回 [-1,…...

SAP BTEs的简介及实现
一、认识BTE BTE(Business Transaction Event)也称之为“业务交易事件”,一般的增强(Tcode:SMOD|CMOD)依旧使用ABAP进行二次开发,然而BTE则提供了RFC调用其它产品的可能(Tcode:FIBF)。BTE的设计思路更加简单,和BADI有点类似。在标准程序中留有…...

如何利用海外主机服务提高网站速度?
网站速度是任何在线业务成功的关键。快速的网站速度可以让用户更快地访问您的网站,增加页面浏览量。对于拥有全球用户的网站而言,选择一个海外主机服务商是提高网站速度的有效方法之一。下面是一些利用海外主机服务(如美国主机、香港主机)提高网站速度的…...

【SpringMVC】 一文掌握 》》》 @RequestMapping注解
个人简介:Java领域新星创作者;阿里云技术博主、星级博主、专家博主;正在Java学习的路上摸爬滚打,记录学习的过程~ 个人主页:.29.的博客 学习社区:进去逛一逛~ RequestMapping注解一、SpringMVC环境准备1.相…...

高三应该怎么复习
高三是学生们备战高考的重要一年,正确有序的复习可以有效地提高复习效率,下面是一些高效复习的方法和建议:1. 制定合理的学习计划和目标高三的学生要制定合理的学习计划和目标,适当的计划和目标可以使学习更有针对性和效率。建议根…...

如何通过C++ 将数据写入 Excel 工作表
直观的界面、出色的计算功能和图表工具,使Excel成为了最流行的个人计算机数据处理软件。在独立的数据包含的信息量太少,而过多的数据又难以理清头绪时,制作成表格是数据管理的最有效手段之一。这样不仅可以方便整理数据,还可以方便…...

Kalman Filter in SLAM (6) ——Error-state Kalman Filter (EsKF, 误差状态卡尔曼滤波)
文章目录0.前言1. IMU的误差状态空间方程2. 误差状态观测方程3. 误差状态卡尔曼滤波4. 误差状态卡尔曼滤波方程细节问题0.前言 这里先说一句:什么误差状态卡尔曼?完全就是在扯淡! 回想上面我们推导的IMU的误差状态空间方程,其实…...

centos7部署KVM虚拟化
目录 centos7部署KVM虚拟化平台 1、新建一台虚拟机 2、系统内的操作 1、修改主机名 2、挂载镜像光盘 3、ssh优化 4、设置本地yum仓库 5、关闭防火墙,selinux 3、安装KVM 4、设置KVM网络 5、KVM部署与管理 6、使用虚拟系统管理器管理虚拟机 创建存储池 …...