数据结构(邓俊辉)学习笔记】词典 03—— 排解冲突(1)
文章目录
- 1. 一山二虎
- 2. 泾渭分明
- 3. 开放定址
- 4. 线性试探
- 5. 赖惰删除
1. 一山二虎

此前我们已经多次指出,对于需要动态维护的散列表冲突是不可避免的,无论你的散列函数设计的有多么精妙,因此我们不得不回答的第二个重要问题就是一旦发生冲突,我们应该如何加以排解?
当然任何一种可行的排解方法都应该是在事先就约定好的预案。
所谓冲突,形象地说也就是一山不容二虎。那么倘若的确有两只老虎呢?用铁丝网将这座山分成两部分,两只老虎各居一侧。这种思路也就是所谓的多槽位法。如果此前的桶单员对应于山,那么每一个 slot 就对应于在这个山中用铁丝网分割出的一个子区域。
在这幅图中,如果这是散列表,那么这就是一个又一个的桶单元。在这里我们将每个桶单元都继续细分为 a、b、c、d 四个槽位。每个桶内部的这些槽位就可以用来存放彼此冲突的若干个词条。

比如这就是一个长度为 23 的散列表,其中每一个桶都被分成了三个槽位。现在我们依次将24个词条插入其中。
可以看到这里,尽管有些词条的确会彼此冲突,但依然可以在对应的桶中和平共处。当然,查找过程需要多出一步,除了需要根据关键码确定对应的统单元地址,还需要在桶中遍历所有的槽位,直到找到目标或者失败。
当然,只要每个桶中槽位的总数能够控制在常数以内,整体的查找效率就不会有实质的降低。
不过这种方法的缺点也是显而易见的,你能看得出来吗?是的,每一桶具体应该细分为多少个槽位?在事先几乎是无法预测的。如果分得过细,就会造成空间上的浪费。而反过来,无论你分得多细,在极端情况下仍有可能在某个特定的桶中发生大规模的冲突。那么面临这一两难的抉择如何破解呢?
2. 泾渭分明

多槽位法在空间效率和时间效率之间的两难处境?我们在学习向量时也曾遇到过,还记得那时的解决办法吗?是的,改用列表。
新的策略,如这幅图(上图)所示,如果这是整个桶数组,那么其中每一个单元都将各自拥有一个对应的列表。而每一个列表都可以用来存放一组彼此冲突的词条。是的,将相互冲突的词条串接起来,也就是所谓的 separate chaining。
来看独立链法的一个实例,依然是一个长度为23的散列表。接下来我们将64个词条插入其中。请留意观察每个桶所对应的列表是如何演化的。
~
相对于与多槽位法独立链法的优势非常明显,比如除了最初的空链表,我们无需预留任何更多的空间。而且表的长度可以根据需要自由的伸缩。只要系统的资源足够,任意多次的冲突都可以解决。
得益于此前对 List 结构的良好封装,我们只需寥寥几句即可实现相应的散列表结构。当然,这种方法的缺点也同样是很明显的。比如这里需要引入额外的指针,而为了生成或销毁节点也需要借助动态内存的申请,相对于常规的操作,此类动态申请操作的时间成本大致要高出两个数量级。
然而这种方法最大的缺陷还不仅于此,你能发现吗?是的,系统的缓存功能。在这里,每桶内部的查找都是沿着对应的列表顺序进行的。然而在此之前,不同列表中各节点的插入和销毁次序完全是随机的。比如可能会是这样(上图中的黄色线条)。
因此,对于任何一个列表而言,其中的节点在物理空间上往往不是连续分布。因此系统很难预测你的访问方向,无法通过有效的缓存加速查找过程。当散列表的规模非常之大,以至于不得不借助 IO 时,这一矛盾就显得更加突出了。 那么为了有效的激活并充分利用系统的缓存功能,我们又当如何继续改进呢?
3. 开放定址

反观独立链法,其采用的是所谓封闭定址策略 closed addressing。也就是说,对于散列表中的任何一个单元,在其所对应的列表中能够存放,而且只能够存放那些与这个桶单元的地址,比如 k ,相冲突的词条。
也就是说每个词条应该属于哪个桶所对应的列表都是在事先已经注定了的。不难理解,只要采用这种策略,就很难保证每组冲突的词条在空间上能够彼此毗邻。 因此后续我们应该放弃这种策略并反其道而行之,也就是采用所谓的开放定址策略 open addressing。
这种策略的特点是散列表所占用的空间在物理上始终是地址连续的一块,相应的所有的冲突都在这块连续的空间中加以排解,而无需向独立链法那样申请额外的空间。没错,所有的散列以及冲突排解都在这样一块封闭的空间内完成。
因此相应的这种策略也可以称作为闭散列 closed hashing。既然是闭散列,那么每一个桶单元都应该面向所有可能的词条开放。也就是说在特定的情况下,每一个词条都有可能存放在任何一个桶中。
当然对于每一个特定的词条具体存放在哪个桶中是有不同的优先级的。其中优先级最高的自然是它本来就应该归属的那个桶。从这个桶开始,所有的桶都按照某种优先级关系排成一个序列。
而在查找对应的词条时,我们也总是从这个序列的起点出发,顺次去尝试每一个桶单元。每个词条所对应的这样一个序列,也称作试探序列、试探链或者查找链。当然,沿查找链的查找,同样有两种结果,或者在某一个特定的桶中找到目标元素,也就是所谓的成功,或者一旦抵达第一个空桶即可报告失败。
那么具体的应该如何来约定查找链呢?
4. 线性试探

查找链的第一种组织方法就是所谓的线性试探。具体来说,所谓的 linear probing 就是一旦发生冲突之后,我们会转而去试探它的后继,后继的后继,以及后继的后继的后继,诸如此类。同样的,最终可能会因为发现目标而报告成功,或者因为抵达某个空桶,而说明查找失败。
可以看到,在如此形势的散列表中,除了数据词条,无需任何附加空间。
而更重要的是,每一查找链都集中在某一局部。因此系统的缓存作用将得到充分的发挥,而对于大规模的数据集,如此更可以有效地减少 IO。当然,这种策略同样也具有它的弱点。
其中重要一点就是为了消除以往的冲突,可能会导致后续发生更多的冲突。来看这样一个实例,考察一个长度为7的散列表。
假设我们需要插入的是 0 1 2 3 7 这样 5 个词条,如果就按照这种顺序依次插入,那么首先是0就位,1 就位,2 和3 也相继就位。为了插入最后的7,我们首先去试探 0 号单元,结果发现它非空,为了排解这一冲突,我们会转而试探它的后继,也就是 1 号单元,情况一样,进而去试探 2 号 3号单元,直到最终在4号单元发现一个空桶,从而将7安置在这个桶中。在这个散列表的生命期内发生冲突的只有一个词条,也就是7。
~
现在再来看另一插入次序,比如将 7 调整到最前端,首先插入它,我们依然开始于一个空的散列表。按照约定的次序,首先将 7 安置在0号桶,没有冲突。既然0号单元已被占用,所以接下来插入 0 必然发生一次冲突,并经过一次试探,最终将 0 安置在 1 号单元。至此1号桶也会被占用。因此接下来词条 1 的插入也会发生一次冲突,并最终将它安置在 2 号桶。这样的故事还会发生多次,具体的也需要在这个位置发现一次冲突之后,再将词条2存入到3号桶,最后依然要经过一次冲突,才能将词条 3 存入 4 号桶。在整个这样的过程中,词条0、词条1 2和3都发生了冲突。前后对比不难发现后一种插入序列所对应的很多冲突本来的确是可以不必发生的。
5. 赖惰删除

以线性试探为代表的开放定址策略在使用时若要支持词条的删除,则需格外的小心。我们来就此做一探讨。按照这种策略先后插入彼此冲突的一组词条都将存放在同一个查找序列中。而更确切地讲,他们应该按照逻辑次序构成整个查找链的一个前缀,其中不得有任何的空桶缝隙。
因此词条的删除操作需要做额外的一些处理。反之如果直接将词条删除,那么被删除的词条所留下的空桶就有可能将查找链切断,从而导致在此之后的词条丢失掉。尽管他们的确存在于散列表中,却如何也访问不到。
针对这一问题,一种简明的方法就是所谓的懒惰删除 lazy removal。也就是说如果在某个桶中此前曾有一个词条,那么在这个词条被删除之后,我们并不是简单地将这个桶清空,而是为其做上一个特殊的标记,比如说R,在这样一个桶所属的每一条查找链中,这类桶单元将根据具体情况可能扮演两种角色。
如果是针对某一特定词条的查找,那么在抵达这个桶时,根据这个标志我们就知道不应该在此中断,而因越过它继续查找下去。反过来,如果我们是为了插入新的词条而寻找一个空桶,那么在首次抵达带有这样一个标志的桶之后,就可以将它等效的视作是一个空桶,并将待插入的新词条径直的插入其中。

应该说针对开放定制策略,懒度删除不仅是不得已而为之的方法,也甚至可以说是针对这种情况的最优方法。因为毕竟在开放定址策略中,每一个同单元都同时属于多个查找链。
相关文章:
数据结构(邓俊辉)学习笔记】词典 03—— 排解冲突(1)
文章目录 1. 一山二虎2. 泾渭分明3. 开放定址4. 线性试探5. 赖惰删除 1. 一山二虎 此前我们已经多次指出,对于需要动态维护的散列表冲突是不可避免的,无论你的散列函数设计的有多么精妙,因此我们不得不回答的第二个重要问题就是一旦发生冲突&…...
HTML5+CSS3-HTML5入门
1.web标准 W3C为web标准化做出了以下事项,主要包括结构,表现和行为。 结构用于对网页的信息进行分类和整理,使用技术包括HTML,XML,XHTML 表现指网页的外在样式,一般包括网页的版式,颜色,字体,…...
谷粒商城实战笔记-138-商城业务-首页-渲染二级三级分类数据
本节的主要内容是在前一节的基础上,提供结构查询出所有的二级、三级分类数据。 一,构造响应体数据结构 后端返回给前端的数据结构是在开发详细设计中应该确定的内容。 分析前端需要的数据结构,后端要将所有一级分类包含的二级和三级分类信…...
git的基础用法
文章目录 前言关联仓库提交代码分支操作账号免密 前言 记录一下git的一些基础用法。 关联仓库 # 初始化 git init# 关联仓库 git remote add origin <仓库地址># 查看当前关联的仓库 git remote -v# 一次只能remote一个,要换需要先删原来的 git remote rem…...
常见中间件漏洞(四、Apache合集)
目录 四、Apache 4.1 CVE-2021-41773 漏洞简介 影响版本 环境搭建 漏洞复现 四、Apache 4.1 CVE-2021-41773 Apache HTTP Server 路径穿越漏洞 漏洞简介 该漏洞是由于Apache HTTP Server 2.4.49版本存在目录穿越漏洞,在路径穿越目录<Directory/>Require all gra…...
HCIE-学习笔记
动态授权加入的成员优先级高于静态绑定的成员; any组(缺省):所有用户或资源,通常用来配置默认规则。any组只能做目的组,不支持配置为源组。 同一个安全组既可以与多条授权规则绑定来表示动态用户࿰…...
【计算机网络】性能指标-带宽和时延(MB、GB、KB、B、byte、bit、Mb/s、Gb/s、b/s等)学习
文章目录 1、单位换算MB、b/s1.1 在计算机领域,大写的B、K、M、G表示1.2 在通信领域,小写的k代表的是1000,不是1024,转换的时候要注意区分 2、带宽3、时延(时间消耗)4、时延带宽积5、往返时延RTT 1、单位换算MB、b/s …...
ANN(Approximate Nearest Neighbor)搜索和索引库到底是什么?
🍉 CSDN 叶庭云:https://yetingyun.blog.csdn.net/ ANN(Approximate Nearest Neighbor)搜索:最近邻搜索是一种在大规模数据集中快速找到与给定查询数据点距离最近的点的算法。与传统的精确最近邻搜索算法相比ÿ…...
勒索软件、供应链攻击等带来的思考!
2023年勒索软件、供应链攻击、地缘政治冲突与黑客活动主义、国家黑客间谍与APT组织活动成为网络安全的热点话题,生成式人工智能技术的武器化更是给动荡的全球网络安全威胁态势增加了不确定性、不对称性和复杂性。 即将到来的2024年,随着网络犯罪的规模化…...
【Nuxt】自定义插件和生命周期
自定义插件 方式一: app.vue // 创建插件(在app.vue中创建全局可以使用 而在某个页面中创建只有该页面可以使用) // 方式一: const nuxtApp useNuxtApp(); nuxtApp.provide("formDate", () > {return "2023-12-12"; }) nuxtAp…...
MySQL的简单介绍
文章目录 数据库关系型数据库非关系型数据”数据库的概念和用途MySQL数据库服务器、数据库和表的关系数据库的创建和删除表创建表修改常见的数据类型和约束字符串类型日期和时间类型PRIMARY KEY使用AUTO_INCREMENT使用UNIQUE使用FOREIGN KEY使用 SQL语言基础SQL语言简介SQL分类…...
leetcode 116.填充每个节点的下一个右侧结点指针
1.题目要求: 给定一个二叉树:struct Node {int val;Node *left;Node *right;Node *next; } 填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL 。初始状态下,所有 ne…...
『 Linux 』网络基础
文章目录 协议分层OSI 七层模型TCP/IP 四层(五层)模型网络协议栈与操作系统的联系报文TCP/IP 通讯过程以太网通信的过程以太网的数据碰撞 协议分层 协议分层是计算机网络中奖网络协议进行组织和管理的方法; 通过将网络通信过程分成多个层次,每个层次负责特定的功能从而简化网络…...
Python酷库之旅-第三方库Pandas(070)
目录 一、用法精讲 281、pandas.Series.dt.daysinmonth属性 281-1、语法 281-2、参数 281-3、功能 281-4、返回值 281-5、说明 281-6、用法 281-6-1、数据准备 281-6-2、代码示例 281-6-3、结果输出 282、pandas.Series.dt.tz属性 282-1、语法 282-2、参数 282-…...
第一篇Linux介绍
目录 1、操作系统 2、Windows和Linux操作系统的区别 3、 Linux 的发行版本 4、 linux 分支 5、 Linux 的含义 6、Linux 特点 1、操作系统 常见操作系统有:Windows、MacOS、Unix/Linux。 类 UNIX Windows:其是微软公司研发的收费操作系统ÿ…...
在Windows编程中,MFC\C++中OnCopyData如何传递基础类型数据?
在C中,OnCopyData 并不是一个标准的C库或框架中的成员函数,它更常见于Windows编程中,特别是使用Win32 API或MFC(Microsoft Foundation Classes)时。OnCopyData 是一个在MFC应用程序中常用于处理来自其他应用程序的WM_C…...
10款超好用的图纸加密软件推荐,2024企业常用图纸加密软件分享
在现代企业中,设计图纸和敏感数据的安全性至关重要。一旦图纸泄露,可能会对企业造成不可估量的损失。因此,选择一款高效、可靠的图纸加密软件显得尤为重要。 1. 安秉图纸加密软件 安秉图纸加密软件是一款专为保护工程图纸和设计文件安全的软…...
BUUCTF [安洵杯 2019]easy_serialize_php 1
打开题目,看到一串php代码,试着代码审计一下,看一下有用信息 可以看出是通过$_SESSION[img]来读取文件 extract可以将数组中的变量导入当前变量表 也就是说我们可以伪造$_SESSION 数组中的所有数据 这里传递一个参数fphpinfo 先用hackbar进…...
前端面试宝典【CSS篇】【5】
在前端开发的世界里,每一次面试都是一次机遇,也是一次挑战。 你是否曾因技术深度不够而错失良机? 或是面对最新的技术趋势感到迷茫? 我们的【前端面试宝典】正是为此而来。 由拥有多年一线实战经验的资深工程师亲自授课,结合最新的行业动态与实战案例,旨在全面提升你的技…...
stem32江科大自学笔记
江科大B站教程连接:【STM32入门教程-2023版 细致讲解 中文字幕】 系列文章目录 提示:收集stem32江科大自学笔记,方便自己和他人查看 视频对应目录STM32入门教程P1-3 [1-2]&[2-1]1.STM32简介、系统介绍、软件安装P4 [2-2]2.基于标准库(库…...
label-studio的使用教程(导入本地路径)
文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...
Java 8 Stream API 入门到实践详解
一、告别 for 循环! 传统痛点: Java 8 之前,集合操作离不开冗长的 for 循环和匿名类。例如,过滤列表中的偶数: List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...
23-Oracle 23 ai 区块链表(Blockchain Table)
小伙伴有没有在金融强合规的领域中遇见,必须要保持数据不可变,管理员都无法修改和留痕的要求。比如医疗的电子病历中,影像检查检验结果不可篡改行的,药品追溯过程中数据只可插入无法删除的特性需求;登录日志、修改日志…...
高等数学(下)题型笔记(八)空间解析几何与向量代数
目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...
Linux云原生安全:零信任架构与机密计算
Linux云原生安全:零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言:云原生安全的范式革命 随着云原生技术的普及,安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测,到2025年,零信任架构将成为超…...
Spring Boot面试题精选汇总
🤟致敬读者 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉 📘博主相关 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...
PL0语法,分析器实现!
简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...
c#开发AI模型对话
AI模型 前面已经介绍了一般AI模型本地部署,直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型,但是目前国内可能使用不多,至少实践例子很少看见。开发训练模型就不介绍了&am…...
GitFlow 工作模式(详解)
今天再学项目的过程中遇到使用gitflow模式管理代码,因此进行学习并且发布关于gitflow的一些思考 Git与GitFlow模式 我们在写代码的时候通常会进行网上保存,无论是github还是gittee,都是一种基于git去保存代码的形式,这样保存代码…...
MySQL:分区的基本使用
目录 一、什么是分区二、有什么作用三、分类四、创建分区五、删除分区 一、什么是分区 MySQL 分区(Partitioning)是一种将单张表的数据逻辑上拆分成多个物理部分的技术。这些物理部分(分区)可以独立存储、管理和优化,…...


