C++ hashtable

news2025/1/6 2:12:12

文章目录

      • 1. 基本概念
      • 2. 哈希函数
      • 3. 哈希冲突及解决方法
      • 开放定址法
      • 链地址法
      • 再哈希法
      • 建立公共溢出区
      • 4. 哈希表的操作实现
      • 5. 内存管理及优化
    • 时间复杂度
      • 理想情况(无哈希冲突或冲突极少)
      • 一般情况(考虑哈希冲突及解决方法)
      • 综合来看

以下是关于哈希表(Hashtable)实现原理的详细介绍:

1. 基本概念

哈希表是一种数据结构,它提供了快速的插入、查找和删除操作,其核心思想是通过一个哈希函数(Hash Function)将数据的关键键(Key)映射为一个固定长度的哈希值(Hash Value),然后利用这个哈希值来确定数据在表中的存储位置,也就是索引(Index)。

2. 哈希函数

  • 作用
    哈希函数负责把各种各样的输入键(通常键的数据类型多样,比如整数、字符串等)转化为一个相对固定范围的整数(哈希值)。理想的哈希函数应该具有以下特性:
    • 确定性:对于相同的输入键,每次计算得到的哈希值都应该相同,保证数据能准确存储和查找。
    • 均匀分布性:尽可能使不同的输入键均匀地映射到哈希表的各个存储位置上,避免出现大量数据集中映射到少数几个位置(即哈希冲突)的情况。
    • 高效性:能够快速地计算出哈希值,减少计算时间成本。
  • 示例
    • 对于整数键,简单的取模运算可以作为一种哈希函数,比如 hash(key) = key % table_size,这里 table_size 是哈希表的大小(即存储位置的总数)。
    • 对于字符串键,一种常见的做法是将字符串中每个字符的ASCII码值进行加权求和后再取模,例如:
int hash(const std::string& key)
 {
    int hash_value = 0;
    for (size_t i = 0; i < key.length(); ++i) {
        hash_value += (int)key[i] * (i + 1);
    }
    return hash_value % table_size;
}

3. 哈希冲突及解决方法

以下是几种常见的解决哈希冲突的方法:

开放定址法

  • 基本原理
    当通过哈希函数计算得到的存储位置已经被占用(即发生哈希冲突)时,按照某种探测规则在哈希表中寻找下一个空闲的存储位置来存放冲突的数据。

  • 具体方式

    • 线性探测
      • 探测规则:在发生冲突后,依次从冲突位置往后顺序查找空闲的存储位置,也就是以固定的步长(通常为1)逐个探测后续的位置。例如,假设哈希表的大小为10,哈希函数计算出某个键对应的哈希值为3,但位置3已被占用,那么就依次检查位置4、5、6……直到找到空闲位置存放该键值对。
      • 优点:实现简单,易于理解和编码实现,不需要额外的数据结构来辅助解决冲突。
      • 缺点:容易出现数据堆积现象,也就是连续的多个键值对可能因为冲突而聚集在一起,形成较长的“聚集链”,这会导致后续的查找、插入操作效率降低,特别是在哈希表比较满的时候更为明显。例如,若很多键值对经过哈希函数计算后初始哈希值都集中在某个范围,后续采用线性探测时就容易在这一范围附近堆积大量数据。
    • 二次探测
      • 探测规则:在发生冲突后,按照与冲突位置的距离呈二次函数关系的顺序去寻找空闲位置。通常先探测与冲突位置间隔为 (12)、(22)、(3^2) 等的位置,即若初始哈希值为 (h),第一次探测位置为 (h + 1^2),第二次探测位置为 (h + 2^2),以此类推(也可以采用类似 (h - 1^2)、(h - 2^2) 等向前探测的方式)。例如,哈希值为5的位置冲突了,先探测 (5 + 1^2 = 6) 位置,若不行再探测 (5 + 2^2 = 9) 位置等。
      • 优点:相对线性探测而言,在一定程度上能够避免数据过度堆积的问题,使得数据在哈希表中的分布更均匀一些,从而提高查找和插入操作的效率。
      • 缺点:不能完全消除数据堆积的情况,而且计算探测位置的过程相对复杂一点,需要进行更多的计算,增加了一定的时间成本;另外,在哈希表快满时,二次探测的效果也会大打折扣,甚至可能出现无法找到空闲位置的情况(尽管这种情况相对少见)。
    • 双重哈希
      • 探测规则:使用两个不同的哈希函数 (h_1(key)) 和 (h_2(key)),当发生冲突时,通过公式 (h(key) = (h_1(key) + i \times h_2(key)) % table_size)(其中 (i) 为探测次数,从 (0) 开始递增,(table_size) 为哈希表的大小)来确定后续的探测位置。例如,第一个哈希函数计算出初始哈希值为 (3),第二个哈希函数计算出值为 (2),当冲突发生时,第一次探测位置是 ((3 + 0 \times 2) % table_size),第二次探测位置是 ((3 + 1 \times 2) % table_size) 等。
      • 优点:通过两个哈希函数的配合,能更有效地避免数据堆积,使数据在哈希表中的分布更加均匀,在合适的参数选择下,可以获得较好的查找和插入性能。
      • 缺点:需要设计和维护两个哈希函数,增加了代码实现的复杂性和计算开销;并且要合理选择两个哈希函数,否则可能达不到预期的效果,甚至可能出现循环探测找不到空闲位置的问题(不过通过合适的设计可以尽量避免这种情况)。

链地址法

  • 基本原理
    把哈希值相同的数据(键值对)用链表连接起来,形成一个个“桶”。当有新的数据产生哈希冲突时,就将其添加到对应哈希值所在的链表末尾。每个链表可以看作是一个存储具有相同哈希值的键值对的集合。

  • 优点

    • 实现相对简单,易于理解和编码实现,只需要在哈希表的基础上额外维护链表结构即可。
    • 处理冲突的效率较高,对于插入操作,只要找到对应的链表头,然后在链表末尾添加新节点即可;查找操作也是先定位到对应的链表,再在链表中顺序查找目标数据,平均查找时间复杂度取决于链表的长度,在链表长度较短时,性能较好。
    • 不会像开放定址法那样出现数据堆积影响整个哈希表性能的问题,因为每个链表相对独立,即使某个哈希值对应的链表较长,对其他哈希值对应的链表以及整个哈希表的其他操作影响不大。
  • 缺点
    需要额外的内存空间来存储链表节点,如果哈希表中存在大量的哈希冲突,导致某些链表很长,那么除了存储键值对本身的数据内存外,还会消耗较多的链表节点内存,可能在一定程度上影响内存的利用效率;另外,在链表较长时,查找操作的时间复杂度会有所增加,不过可以通过一些优化手段(如将链表转换为更高效的数据结构,像红黑树等,当链表长度超过一定阈值时进行转换)来缓解这个问题。

再哈希法

  • 基本原理
    当发生哈希冲突时,换用另一个哈希函数重新计算哈希值,期望通过新的哈希函数能够得到一个空闲的存储位置。如果新的哈希值仍然冲突,就继续更换哈希函数再次计算,直到找到空闲位置或者达到设定的最大尝试次数等限制条件为止。

  • 优点
    可以根据不同的冲突情况灵活选择不同的哈希函数来优化数据的存储位置,有可能找到更合适的、分布更均匀的存储方式,减少冲突带来的影响。

  • 缺点
    需要准备多个不同的哈希函数,增加了代码的复杂性和维护成本;而且频繁更换哈希函数会带来较高的计算开销,尤其是如果尝试多次都无法有效解决冲突时,会严重影响哈希表操作的整体效率;此外,很难保证换用的哈希函数一定能找到空闲位置,可能陷入不断尝试却始终无法妥善解决冲突的困境。

建立公共溢出区

  • 基本原理
    在哈希表之外,额外开辟一块连续的存储区域作为公共溢出区。当发生哈希冲突时,将冲突的数据统一存放到这个公共溢出区中,可以通过记录指针或者索引等方式来关联原哈希表中的位置和公共溢出区中的存储位置,便于后续查找等操作。

  • 优点
    能够保证哈希表本身的结构相对简单,不会因为解决冲突而使哈希表内部的存储结构变得复杂,便于对哈希表的其他操作(如遍历等)进行管理;而且对于公共溢出区,可以采用更灵活的存储和管理方式,比如根据数据量动态调整其大小等。

  • 缺点
    增加了额外的存储区域,需要更多的内存空间来支持;查找操作相对复杂一些,需要先在哈希表中查找,如果没找到再去公共溢出区查找,增加了一次额外的查找步骤,可能会影响查找的效率,尤其是在公共溢出区数据量较大时更为明显。

不同的哈希冲突解决方法各有优缺点,在实际应用中需要根据具体的使用场景、数据特点以及性能要求等因素综合考虑来选择合适的方法。

4. 哈希表的操作实现

  • 插入操作
    • 首先,通过哈希函数计算要插入数据的键对应的哈希值,确定其在哈希表中的大致存储位置。
    • 如果该位置没有数据(不存在哈希冲突),则直接将数据存储在该位置;如果发生哈希冲突,根据所采用的冲突解决方法(如开放定址法进行探测找空闲位置,或者链地址法添加到对应链表末尾等)来妥善放置数据。
  • 查找操作
    • 同样先利用哈希函数算出要查找数据的键对应的哈希值,定位到对应的存储位置或“桶”。
    • 如果是采用开放定址法解决冲突,按照相应的探测规则查找目标数据;如果是链地址法,就在对应的链表中逐个查找,直到找到目标数据或者遍历完链表确定未找到。
  • 删除操作
    • 先通过哈希函数找到目标数据所在位置,再依据冲突解决方式来进行删除。对于开放定址法,要谨慎处理删除后的空闲位置标记等问题,避免影响后续查找等操作的正确性;对于链地址法,直接从链表中删除相应节点即可。

5. 内存管理及优化

  • 在内存中,哈希表的存储结构根据实现方式有所不同。采用开放定址法时,通常就是一个连续的数组结构来存放数据;采用链地址法时,除了有用于存储数据大致位置的数组(存放指向链表头的指针等),还需要为各个链表分配额外的内存空间用于存储节点。
  • 为了优化哈希表的性能,可以根据实际应用场景合理选择哈希函数、冲突解决方法以及哈希表的大小等参数。例如,根据预估的数据量来调整哈希表大小,避免过于频繁的哈希冲突;定期对哈希表进行重构(比如重新计算哈希值、调整存储位置等)来优化数据分布,提高整体效率。

总之,哈希表通过巧妙地运用哈希函数和解决哈希冲突的机制,实现了高效的数据存储、查找和删除操作,在众多领域如数据库、编译器、缓存系统等都有着广泛的应用。

哈希表的时间复杂度在不同操作以及不同情况下有所差异,以下是对其插入、查找和删除操作时间复杂度的详细分析:

时间复杂度

理想情况(无哈希冲突或冲突极少)

  • 插入操作
    在理想状态下,通过哈希函数能将每个键均匀且唯一地映射到哈希表的不同位置,插入操作只需要计算键对应的哈希值,然后将数据存放到对应的位置即可,这个过程通常可以在常数时间 (O(1)) 内完成。例如,有一个足够大且设计良好的哈希表,插入少量不同的键值对时,几乎不会产生哈希冲突,每次插入都能迅速定位到空闲位置进行存储。
  • 查找操作
    同样,当不存在哈希冲突时,查找一个键对应的键值对,只需根据键计算出哈希值,然后直接访问该哈希值对应的位置就能确定是否找到目标,时间复杂度也是 (O(1))。就好比在一个完美的哈希表中找某个元素,一次定位就能知晓结果。
  • 删除操作
    类似于插入和查找操作,若没有哈希冲突,通过哈希值定位到要删除的数据所在位置后,直接进行删除处理,时间复杂度同样为 (O(1))。

一般情况(考虑哈希冲突及解决方法)

  • 开放定址法
    • 插入操作
      平均情况下,插入操作的时间复杂度接近 (O(1)),不过在最坏情况下,如果哈希表接近满或者哈希函数设计不佳导致大量冲突,插入操作可能需要多次探测空闲位置,时间复杂度会退化为 (O(n)),其中 (n) 是哈希表的大小。例如,采用线性探测且不断有数据集中在少数几个哈希值对应的位置附近,插入新数据时可能要线性遍历很长一段已占用的位置去寻找空闲处。
    • 查找操作
      平均情况下,查找操作的时间复杂度大约是 (O(1)),但在最坏情况下,比如数据聚集严重,查找一个键可能需要遍历哈希表中的大部分位置,时间复杂度会达到 (O(n))。
    • 删除操作
      平均时间复杂度接近 (O(1)),然而在最坏情况下,由于要谨慎处理删除后留下的空闲位置(避免影响后续查找等操作的正确性,可能需要对后续数据进行重新整理等操作),时间复杂度也可能退化为 (O(n))。
  • 链地址法
    • 插入操作
      插入操作首先要通过哈希函数定位到对应的链表,这个过程通常是 (O(1)),然后在链表末尾添加节点,平均时间复杂度取决于链表的平均长度,设链表平均长度为 (k),则插入操作的平均时间复杂度为 (O(1 + k)),在链表较短时,可近似看作 (O(1))。例如,一个哈希表采用链地址法,大部分链表长度较短,插入新键值对时找到对应链表后很快就能添加进去。
    • 查找操作
      先通过哈希函数定位到链表是 (O(1)),然后在链表中查找目标键值对,平均时间复杂度同样取决于链表平均长度 (k),为 (O(1 + k)),若 (k) 较小,可认为接近 (O(1)),但在链表很长时,时间复杂度会升高,最坏情况下如果链表长度达到 (n)(哈希表大小),时间复杂度就是 (O(n))。
    • 删除操作
      先定位到链表是 (O(1)),然后在链表中删除相应节点,平均时间复杂度取决于链表平均长度 (k),为 (O(1 + k)),类似查找操作,在不同链表长度情况下表现不同。

综合来看

哈希表在设计良好(哈希函数合理、冲突解决方法得当、哈希表大小合适等)的情况下,插入、查找和删除操作的平均时间复杂度都能接近 (O(1)),这使得它在很多需要快速查找、插入和删除数据的场景中有着广泛的应用。不过在极端或不良的条件下(比如哈希函数选择不当、数据量远超哈希表承载能力等),时间复杂度可能会恶化,所以在实际应用中要综合考虑各方面因素来优化哈希表的性能。

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

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

相关文章

数据结构(ing)

学习内容 指针 指针的定义&#xff1a; 指针是一种变量&#xff0c;它的值为另一个变量的地址&#xff0c;即内存地址。 指针在内存中也是要占据位置的。 指针类型&#xff1a; 指针的值用来存储内存地址&#xff0c;指针的类型表示该地址所指向的数据类型并告诉编译器如何解…

实践:事件循环

实践&#xff1a;事件循环 代码示例 console.log(1); setTimeout(() > console.log(2), 0); Promise.resolve(3).then(res > console.log(res)); console.log(4);上述的代码的输出结果是什么 1和4肯定优先输出&#xff0c;因为他们会立即方式堆栈的执行上下文中执行&am…

从零开始开发纯血鸿蒙应用之逻辑封装

从零开始开发纯血鸿蒙应用 一、前言二、逻辑封装的原则三、实现 FileUtil1、统一的存放位置2、文件的增删改查2.1、文件创建与文件保存2.2、文件读取2.2.1、读取内部文件2.2.2、读取外部文件 3、文件删除 四、总结 一、前言 应用的动态&#xff0c;借助 UI 响应完成&#xff0…

ESP32 I2S音频总线学习笔记(一):初识I2S通信与配置基础

文章目录 简介为什么需要I2S&#xff1f;关于音频信号采样率分辨率音频声道 怎样使用I2S传输音频&#xff1f;位时钟BCLK字时钟WS串行数据SD I2S传输模型I2S通信格式I2S格式左对齐格式右对齐格式 i2s基本配置i2s 底层API加载I2S驱动设置I2S使用的引脚I2S读取数据I2S发送数据卸载…

CSS 中 content换行符实现打点 loading 正在加载中的效果

我们动态加载页面内容的时候&#xff0c;经常会使用“正在加载中…”这几个字&#xff0c;基本上&#xff0c;后面的 3 个点都是静态的。静态的问题在于&#xff0c;如果网络不流畅&#xff0c;加载时间比较长&#xff0c;就会给人有假死的 感觉&#xff0c;但是&#xff0c;如…

25考研王道数据结构课后习题笔记

声明&#xff1a;以下内容来自于B栈知名up主–白话拆解数据结构 回答&#xff1a;为什么要做这个&#xff0c;因为我这个学期学完了数据结构&#xff0c;而且这个数据结构是408的重头&#xff0c;为什么选择25的&#xff0c;因为这个25考研刚刚结束&#xff0c;25相对成熟&…

小程序发版后,强制更新为最新版本

为什么要强制更新为最新版本&#xff1f; 在小程序的开发和运营过程中&#xff0c;强制用户更新到最新版本是一项重要的策略&#xff0c;能够有效提升用户体验并保障系统的稳定性与安全性。以下是一些主要原因&#xff1a; 1. 功能兼容 新功能或服务通常需要最新版本的支持&…

GRAPE——RLAIF微调VLA模型:通过偏好对齐提升机器人策略的泛化能力(含24年具身模型汇总)

前言 24年具身前沿模型大汇总 过去的这两年&#xff0c;工作之余&#xff0c;我狂写大模型与具身的文章&#xff0c;加之具身大火&#xff0c;每周都有各种朋友通过CSDN私我及我司「七月在线」寻求帮助/指导(当然&#xff0c;也欢迎各大开发团队与我司合作共同交付&#xff09…

0xc0000020错误代码怎么处理,Windows11、10坏图像错误0xc0000020的修复办法

“0xc0000020”是一种 Windows 应用程序错误代码&#xff0c;通常表明某些文件缺失或损坏。这可能是由于系统文件损坏、应用程序安装或卸载问题、恶意软件感染、有问题的 Windows 更新等原因导致的。 比如&#xff0c;当运行软件时&#xff0c;可能会出现类似“C:\xx\xxx.dll …

pycharm+anaconda创建项目

pycharmanaconda创建项目 安装&#xff1a; Windows下PythonPyCharm的安装步骤及PyCharm的使用-CSDN博客 详细Anaconda安装配置环境创建教程-CSDN博客 创建项目&#xff1a; 开始尝试新建一个项目吧&#xff01; 选择好项目建设的文件夹 我的项目命名为&#xff1a;pyth…

基于Pytorch和yolov8n手搓安全帽目标检测的全过程

一.背景 还是之前的主题&#xff0c;使用开源软件为公司搭建安全管理平台&#xff0c;从视觉模型识别安全帽开始。主要参考学习了开源项目 https://github.com/jomarkow/Safety-Helmet-Detection&#xff0c;我是从运行、训练、标注倒过来学习的。由于工作原因&#xff0c;抽空…

【PDF物流单据提取明细】批量PDF提取多个区域内容导出表格或用区域内容对文件改名,批量提取PDF物流单据单号及明细导出表格并改名的技术难点及小节

相关阅读及下载&#xff1a; PDF电子物流单据&#xff1a; 批量PDF提取多个区域局部内容重命名PDF或者将PDF多个局部内容导出表格&#xff0c;具体使用步骤教程和实际应用场景的说明演示https://mp.weixin.qq.com/s/uCvqHAzKglfr40YPO_SyNg?token720634989&langzh_CN扫描…

JavaWeb开发(五)Servlet-ServletContext

1. ServletContext 1.1. ServletContext简介 1.1.1. ServletContext定义 ServletContext即Servlet上下文对象&#xff0c;该对象表示当前的web应用环境信息。 1.1.2. 获取ServletContext对象: &#xff08;1&#xff09;通过ServletConfig的getServletContext()方法可以得到…

长时间序列预测算法---Informer

目录 一、传统的 Transformer 模型二、Informer原理2.1 Attention计算2.2 “积极”的Q筛选2.2.1 KL散度2.2.2 “懒惰”的q处理 2.3 Encoder结构2.4 Decoder结构2.4.1 Transformer的Decoder操作2.4.2 Informer的Decoder操作 2.5 Informer模型的改进 三、模型应用 时间序列相关参…

点击取消按钮,console出来数据更改了,页面视图没有更新

点击取消按钮&#xff0c;console出来数据更改了&#xff0c;页面视图没有更新 前言 实现效果&#xff1a;点击取消按钮&#xff0c;页面视图全部为空&#xff0c; 遇到的问题&#xff1a; 点击取消按钮&#xff0c;console出来数据更改了&#xff0c;SchemaJson 都是默认值啦…

RFID手持机与RFID工业平板在仓储物流管理系统中的选型

概述 随着物联网技术在仓储物流管理系统中的普及&#xff0c;RFID手持机与RFID工业平板作为基于RFID技术手持式读写器的两种重要终端设备形态&#xff0c;得到了广泛应用。尽管RFID手持机与RFID工业平板都具备读写 RFID标签的基本功能&#xff0c;使用场景较为类似&#xff0c…

UML之泛化、特化和继承

在UML&#xff08;统一建模语言&#xff09;中&#xff0c;泛化&#xff08;Generalization&#xff09;和特化&#xff08;Specialization&#xff09;是面向对象思想中继承&#xff08;Inheritance&#xff09;关系的重要概念&#xff0c;它们描述类与类&#xff08;或用例与…

vue 修改vant样式NoticeBar中的图标,不用插槽可以直接用图片

使用文档中是可以直接使用图片链接的 :left-icon"require(../../assets/newImages/noticeImg.png)" <html> .... <NoticeBarmode""color"#C6C6C6"background""v-if"global_info.site_bulletin":left-icon"r…

【漫话机器学习系列】028.CP

Mallows’ Cp&#xff1a;标准化公式解析与应用 Mallows’ Cp 是一种常用的模型选择工具&#xff0c;用于在一系列候选模型中权衡拟合度和复杂性&#xff0c;帮助我们选择性能最优的模型。本文将基于其标准化公式展开详细解析&#xff0c;并探讨其应用场景、实现方法、优点与局…

vs 2022 中xml 粘贴为Class 中,序列化出来的xml 的使用

上图是visual studio 2022 中使用的粘贴功能的菜单位置 在生成的xml 中&#xff0c;有些是类似如下类型的 [System.Serializable] [System.Xml.Serialization.XmlType] public class Item {private bool isVisibleField;private bool isVisibleFieldSpecified;[System.Xml.Se…