散列(哈希)表

2023-10-30

1.如何构造散列函数,总结三点散列函数设计的基本要求:

1)散列函数计算得到的散列值是一个非负整数        //下标从0开始

2)如果key1 = key2,那么hash(key1)==hash(key2) //相同的key经过hash 得到的散列值应该是相等的。

3)  如果key1 != key2,  那么hash(key1) != hash(key2)//这个是理想情况下,越接近这种就证明hash函数设计的完美,会有相等的情况,就是发生了散列冲突

2.散列冲突

散列冲突的解决方法有两类,开放寻址和链表法

1.开发寻址

1) 线性探测

 

线性探测查找和插入差不多,先计算散列值,如果此时已经存在元素了,则往下,直到找到相等的值或者找到空的位置。 但如果空的位置是我们后删除的呢,如下图

1的位置被我们删除的,如果不做任何处理,那么即使y在2的位置上,也找不到,到1的时候发现这个位置是空的,说明y没插入过。 所以我们要加上一个deleted标志,遇到这个标志后要继续往下走即可。

线性探测存在很大的问题。当散列表中插入的数据越来越多时,散列冲突发生的可能性就会越来越大,空闲位置会越来越少,线性探测的时间就会越来越久。极端情况下,我们可能需要探测整个散列表,所以最坏情况下的时间复杂度为 O(n)。同理,在删除和查找时,也有可能会线性探测整张散列表,才能找到要查找或者删除的数据。

对于开放寻址冲突解决方法,除了线性探测方法之外,还有另外两种比较经典的探测方法,二次探测(Quadratic probing)和双重散列(Double hashing)。

所谓二次探测,跟线性探测很像,线性探测每次探测的步长是 1,那它探测的下标序列就是 hash(key)+0,hash(key)+1,hash(key)+2……而二次探测探测的步长就变成了原来的“二次方”,也就是说,它探测的下标序列就是 hash(key)+0,hash(key)+12,hash(key)+22……

所谓双重散列,意思就是不仅要使用一个散列函数。我们使用一组散列函数 hash1(key),hash2(key),hash3(key)……我们先用第一个散列函数,如果计算得到的存储位置已经被占用,再用第二个散列函数,依次类推,直到找到空闲的存储位置。

不管采用哪种探测方法,当散列表中空闲位置不多的时候,散列冲突的概率就会大大提高。为了尽可能保证散列表的操作效率,一般情况下,我们会尽可能保证散列表中有一定比例的空闲槽位。我们用装载因子(load factor)来表示空位的多少。

装载因子的计算公式是:

 

散列表的装载因子 = 填入表中的元素个数 / 散列表的长度

装载因子越大,说明空闲位置越少,冲突越多,散列表的性能会下降。

 

2.链表法

如图所示:位置冲突后,用链表降散列值相等的元素关联起来。

插入的时间复杂度为O(1)显而易见

那么查找或者删除的复杂度是?

实际上这两个操作的时间复杂度跟链表的长度k成正比,也就是O(k)。对于散列比较均匀的散列函数来说,理论上讲,k=n/m,其中n表示散列中数据的个数,m表示散列表中的槽数。

3.如何设计工业级散列表

1.初始大小

hashmap默认的初始大小是16,当然这个默认值是可以设置的,如果事先知道大概的数据量有多大,可以通过修改默认初始值大小,减少动态扩容的次数或者降低空间的浪费。

2.装载因子和动态扩容

最大装载因子默认是0.75,当hashmap中元素个数超过0.75*capacity(表示散列表的容量)的时候,就会启动扩容,每次扩容都会扩容为原来的两倍大小。

动态扩容如下图所示

并不是扩容后,就全都一下把数据hash到新的内存空间上,而是每次插入新元素的时候去把旧的一个重新散列到新的散列表上,查找的时候就是双读,先新后旧。

插入一个数据,最好情况下,不需要扩容,最好时间复杂度是O(1),最坏情况下,散列表装载因子过高,启动扩容,我们需要重新申请内存空间,重新计算哈希位置,并且搬移数据,所以时间复杂度是O(n)。用摊还分析法,时间复杂度接近最好情况,就是O(1)。

3.散列冲突解决方法:

hashmap底层采用链表法来解决冲突。即使负载因子和散列函数设计得再合理,也免不了会出现拉链过长的情况,一旦出现拉链过长,则会严重影响hashmap的性能。

于是为了对hashmap做进一步优化,我们引入了红黑树。而当链表长度太长(默认超过8)时,链表就转换为红黑树。我们可以利用红黑树快速增删该查的特点,提高性能。当红黑树节点个数少于8个的时候,又会将红黑树转化为链表。因为在数据量较小的情况下,红黑树要维护平衡,比起链表来,性能上的优势并不明显。

如何实现一个LRU算法

我们需要维护一个按照访问时间从大到小有序排列的链表结构。因为缓存大小有限,当缓存空间不够,需要淘汰一个数据的时候,我们直接将链表头部的结点删除。当要缓存某个数据的时候,先在链表中查找这个数据。如果没有找到,则直接将数据放到链表的尾部;如果找到了,我们就把它移到链表的头部。因为查找数据需要遍历链表,所以单纯的用链表实现的LRU缓存淘汰算法的时间复杂度很高,是O(n)

实际上:一个缓存系统主要包含下面这几个操作

1.往缓存中添加一个数据

2.从缓存中删除一个数据

3.在缓存中查找一个数据

这三个操作都涉及查找操作,如果单纯地采用链表的话,时间复杂度只能是O(n).如果我们将散列表和链表两种数据结构组合使用,可以将这三个操作的时间复杂度都降低到O(1),具体的结构如下:

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

散列(哈希)表 的相关文章

  • Raspberry Pi和Python-OpenCV-TensorFlow卷积神经网络热成像人物检测

    构建逻辑 定期从红外摄像机捕获快照 对其进行标准化 并将其存储在某处 标记图片 检测到人物存在 检测到人物不存在 并在其上训练模型 在树莓派上部署模型并运行定期针对新捕获的图像进行检测 房间里的人是否存在 物料清单 通讯选择 系统准备 捕捉
  • 永兴的tensorflow笔记-6 激活函数

    一 基本神经元 神经元模型 用数学公式表示为 f 为激活函数 w为权重 b为偏置 人工神经网络是由神经元构成的 二 什么是激活函数 将线性函数转变为非线性函数 负责将神经元的输入映射到输出端 激活函数 Activation function
  • bootstrap-table遇到的问题

    1 controller层 queryParams 参数提交不过去 是因为 bootstrap table js中默认是contentType application json 我们必须改成 contentType application
  • IOC和注解

    想要学好spring 必须时时刻刻想着 spring的本质就是一个容器 放java对象的容器 java对象在spring容器中也叫做bean对象 文章目录 一 spring介绍 1 什么是框架 2 框架的作用 在这里插入图片描述 https
  • 行业合规标准MISRA如何帮助C/C++代码程序员高效地编写代码?

    MISRA标准包含编写软件的准则和代码规则 汽车 航空航天和国防 医疗 工业自动化和铁路等行业都使用该标准来帮助他们的开发人员编写源代码 以确保软件的安全 安保和可靠性 由于嵌入式软件工程师使用C和C 编程语言来编写安全关键型软件的代码 M
  • FPGA原理与结构——FIFO IP核的使用与测试

    一 前言 本文介绍FIFO Generator v13 2 IP核的具体使用与例化 在学习一个IP核的使用之前 首先需要对于IP核的具体参数和原理有一个基本的了解 具体可以参考 FPGA原理与结构 FIFO IP核原理学习https blo
  • 静态路由详解

    静态路由 是一种路由的方式 路由项 routing entry 由手动配置 而非动态决定 与动态路由不同 静态路由是固定的 不会改变 即使网络状况已经改变或是重新被组态 一般来说 静态路由是由网络管理员逐项加入路由表 优点 使用静态路由的另
  • 二叉树的五种遍历方式

    目录 1 前序遍历 1 递归实现前序遍历 2 非递归实现前序遍历 2 中序遍历 1 递归实现中序遍历 2 非递归实现中序遍历 3 后序遍历 1 递归实现后序遍历 2 非递归实现后序遍历 4 层序遍历 5 之字形遍历 二叉树是一种重要的数据结
  • conda冗余package的清理(.conda/pkgs)

    今天跑一个论文的代码 结果环境给我报错 说我numpy的版本太高 我删掉重新pip install 结果又出其他问题 问了学长 学长说是把tensorflow和pytorch放一起了 冲突 又是一个血的教训 只好重新配环境 结果一看自己的p
  • 一天走七万步是什么体验?

    嗨大家好 我是南瓜的好朋友西瓜 最近是迷恋上运动的 每天跑不够5w步不带停的那种 为什么这么说呢 jio要跑断的西瓜每天7万步是什么体验呢 当然是沉浸在运动的欢畅中空调的庇护下 说正经的 大家好 今天带给大家的是一键称霸微信运动排行榜的超级
  • f452虚拟服务器,F460 F452 获取超级密码 解决 LOID 注册断线 保留telnet 无需ttl 不用拔光纤...

    有台F460需要改成拨号 找资料参考了以下两篇 http www hackblog cn post 80 html 还是遇到问题获取不到超级密码 第一个是一注册LOID就掉线 第二个是系统是默认只读无法写到httpd目录里 想着断线会不会是
  • 组播基础实验,基于ENSP

    实验拓扑 实验步骤 安装VLS 一个媒体播放器 在进行ENSP的组播实验中 扮演组播源播放视频 组成员接受视频的作用 在做组播实验之前 需要完成单播的基础建设 IGP需要先部署好 保证接受者和源是可达的 在最后一跳路由器上和组成员之间运行I
  • 统计从键盘输入一行字符的个数.c

    统计从键盘输入一行字符的个数 一个字符代表一个 一个汉字代表两个 思想 当输入的字符不等于键盘上的enter键时 每输入一个字符就加1 include
  • Java基础 :HashSet(使用方法详解)

    Java HashSet HashSet 基于 HashMap 来实现的 是一个不允许有重复元素的集合 HashSet 允许有 null 值 HashSet 是无序的 即不会记录插入的顺序 HashSet 不是线程安全的 如果多个线程尝试同
  • ORA-01795: 列表中的最大表达式数为1000的解决方法

    ORA 01795 列表中的最大表达式数为1000的解决方法 IN中的数据量不能超过1000条 解决方案 把条件分成多个少于1000的IN即 DELETE FROM T MM SECTION SITE UPDATE WHERE T T MM
  • python绘图点样式

    plt plot x y x markersize 3 linestyle color darkgreen
  • unity体感游戏--接钻石游戏(三)游戏物体碰撞得分

    u3d的碰撞函数是OnTriggerEnter 代码如下 using UnityEngine using System Collections public class onCollider MonoBehaviour public Gam
  • 阿里云ECS服务器使用教程 新手上云好助手

    随着普及率越来越高 云服务器已经成为企业及个人用户开展网络业务的首选了 阿里云服务器在国内起步早 现在的用户数量是国内第一 全球五强 因为初次接触云服务 所以阿里云服务器的使用方法 对于很多新手小白还不太了解 下面老魏就讲解阿里云服务器的简

随机推荐

  • 多种方式解决Java控制台报错 java.util.LinkedHashMap cannot be cast to.....

    问题描述 今天在使用RestTemplate调用服务的时候 因为服务提供者返回的是一个List集合 所以在使用消费者调用的时候 restTemplate getForObject 期待返回的类型直接写成了List class 相关代码如下
  • 写5个数学建模的经典模型案例和代码

    1 线性规划模型案例 生产计划 假设一家工厂生产两种产品A和B 每个月有100个工作日 每个工作日可以生产200个A产品或150个B产品 A产品售价为200元 个 B产品售价为300元 个 每个月至少需要保证收入不低于200000元 制定生
  • 随机效应估算与固定效应估算_固定效应还是随机效应——Hausman检验.PPT

    固定效应还是随机效应 Hausman检验 7 3 随机效应模型估计 7 3 2 用EViews7 2估计随机效应模型 数据导入 数据结构转换以及模型设定与固定效应模型估计一样 不同的是在panel option的cross section中
  • Vue之父子组件通过事件通信

    前言 组件间传值的章节我们知道父组件给子组件传值的时候 使用v bind的方式定义一个属性传值 子组件根据这个属性名去接收父组件的值 但是假如子组件想给父组件一些反馈呢 就不能使用这种方式来 而是使用事件的方式 父组件通过注册这个事件的监听
  • 使用Docker安装Mysql数据库,及国内常用docker镜像地址

    1 安装docker 输入 yum install y docker 2 配置docker镜像地址 输入 vi etc docker daemon json 在配置文件中写入 registry mirrors http hub mirror
  • 拷贝复制命令行输出放在系统剪贴板上

    转载自 http oldratlee com post 2012 12 23 command output to clip 为什么要这么做 直接把命令的输出 比如 grep awk sed find 或是你的程序输出结果 放到剪切板上 这么
  • Redis使用总结(三、缓存击穿问题)

    什么是缓存击穿 在谈论缓存击穿之前 我们先来回忆下从缓存中加载数据的逻辑 如下图所示 因此 如果黑客每次故意查询一个在缓存内必然不存在的数据 导致每次请求都要去存储层去查询 这样缓存就失去了意义 如果在大流量下数据库可能挂掉 这就是缓存击穿
  • 低功耗设计之门控时钟

    门控时钟一般是用于多比特的数据 因为门控单元也有资源消耗 需要耗电 一般数据位宽超过3bit才会有收益 1 与门 或门 门控时钟 门控时钟的控制信号可以选用高电平有效或低电平有效 若控制信号高电平有效 可以使用与门进行时钟门控 时钟被关闭时
  • Qt 子窗口和父窗口,子窗口和子窗口控件获取

    文章目录 前言 一 代码 二 局限性 前言 Qt开发过程中 难免会遇到子窗口需要获取父窗口某个控件的状态 或者是子窗口需要获取另外一个子窗口某个控件的状态 之前用过回调 全局变量 信号和槽连接 但是都太麻烦了 后面研究出一种简单的方法 会有
  • A--玉米大炮--2022河南萌新联赛第(三)场:河南大学

    输入 3 3 1 1 2 2 3 3 输出 0 说明 开始时 小蓝控制所有大炮立即发射炮弹 僵王博士受到 666 点伤害 直接被击溃 示例2 输入 2 20 5 1 5 3 输出 2 说明 开始时 小蓝控制所有大炮立即发射炮弹 僵王博士受到
  • Linux终端配置——zsh+插件优化

    文章目录 安装Zsh Ubuntu版本 安装插件 更改默认shell 并配置插件和主题 启动插件和主题 刚入Linux的萌新 发现Linux虽然牛 但是这个终端也太不智能了 既不美观也不智能 所以本篇博客可以通过安装zsh进行简单的优化 包
  • react16常见api以及原理剖析

    Vue 与 React 两个框架的粗略区别对比 Vue 的优势包括 模板和渲染函数的弹性选择 简单的语法及项目创建 更快的渲染速度和更小的体积 React 的优势包括 更适用于大型应用和更好的可测试性 同时适用于 Web 端和原生 App
  • sqli练习1-5关(超详细)

    此博客仅用来记录我的学习过程 我会把每一步都记录下来 sqlli 练习 第一关 手工注入 第二关 手工注入 第三关 手工注入 第四关 手工注入 第五关 手工注入 第一关 手工注入 判断是否存在注入 第二关 手工注入 判断是否存在注入以及注入
  • RBAC权限详解

    1 传统的权限设计 首先 我们先了解下什么是传统的权限设计 从上面的图中 我们发现 传统的权限设计是对每个人进行单独的权限设置 但这种方式已经不适合目前企业的高效管控权限的发展需求 因为每个人都要单独去设置权限 2 RBAC权限详解 基于此
  • 有了这款可视化算法项目,算法不再难!

    我的新书 Android App开发入门与实战 已于2020年8月由人民邮电出版社出版 欢迎购买 点击进入详情 GitHub严选 每天推荐一个GitHub优质开源项目 从不奢求生活能给予我最好的 只是执着于寻求最适合我的 大家好 我是严选哥
  • SpringBoot整合SpringData JPA详解

    本篇文章主要记录SpringBoot整合SpringData JPA 感兴趣的小伙伴和小编一起来学习吧 目录 1 添加依赖 2 编写一个实体类 3 编写一个Dao接口来操作实体类对应的数据表 4 application配置 5 编写接口 1
  • 分布式秒杀案例讲解教程文档

    程序员ken 一 准备工作 1 1 vmware软件安装 虚拟机 相关教程 http c biancheng net view 714 html 网络配置这块 1 进入网络配置文件目录 cd etc sysconfig network sc
  • 产品经理针对用户访谈获得的信息该如何理解吸收

    核心 他人的负反馈 吐槽 要认可 他人的正反馈 认可 要谨慎 以产品规划阶段为例 我们打算做一个产品 为了论证市场价值 我们会找相关的客用户开展用户访谈 根据事先准备的访谈大纲开展访谈 收集信息很容易 如何筛选 分析信息 这才是难点和关键
  • UE4数据写入Json格式

    用UE4写入Json很简单只需要使用 TSharedPtr
  • 散列(哈希)表

    1 如何构造散列函数 总结三点散列函数设计的基本要求 1 散列函数计算得到的散列值是一个非负整数 下标从0开始 2 如果key1 key2 那么hash key1 hash key2 相同的key经过hash 得到的散列值应该是相等的 3