第九节:哈希表(初阶)

1. 哈希表的核心概念

哈希表(Hash Table)是一种通过哈希函数将键(Key)映射到存储桶(Bucket)的数据结构,核心目标是实现快速查找、插入和删除操作。其核心特点如下:

  • 哈希函数:将任意长度的键转换为固定长度的索引(通常取模运算)。
    index = hash(key) % bucket_count;
  • 冲突解决:当不同键映射到同一索引时,通过链表、开放寻址法等方式处理冲突。
  • 平均时间复杂度
    • 插入、查找、删除:​O(1)(理想情况,无冲突)。
    • 最坏情况(所有键冲突):​O(n)(退化为链表)。


2. C++ 哈希表的实现容器

C++ STL 提供了两种主要的哈希表容器:

容器特性适用场景
unordered_map键值对存储,支持快速访问、插入、删除。键唯一,值可修改。需要键值对且频繁查询的场景
unordered_set存储唯一键,仅支持快速查询、插入、删除。需要快速判断元素是否存在
示例代码
#include <iostream>
#include <unordered_map>
#include <unordered_set>int main() {// std::unordered_map 示例std::unordered_map<std::string, int> word_count;word_count["hello"] = 5;word_count["world"] = 3;std::cout << "Count of 'hello': " << word_count["hello"] << std::endl; // 输出 5// std::unordered_set 示例std::unordered_set<char> vowels = {'a', 'e', 'i', 'o', 'u'};std::cout << std::boolalpha << vowels.count('a') << std::endl; // 输出 truereturn 0;
}

3. 哈希表的内部机制

3.1 哈希函数
  • 默认哈希函数:STL 使用模板类 std::hash,但对自定义类型需手动定义哈希函数。
  • 自定义哈希函数:通过特化 std::hash 或传递自定义哈希函数对象。
    // 自定义哈希函数示例(字符串)
    namespace std {template<> struct hash<string> {size_t operator()(const string& s) const {size_t h = 0;for (char c : s) h = h * 131 + c; // 简单哈希算法return h;}};
    }
3.2 冲突解决策略

C++ 哈希表采用链地址法​(Chaining)处理冲突:

  • 每个桶(Bucket)存储一个链表(或动态数组),所有哈希到同一索引的键值对按顺序存储。
  • 负载因子​(Load Factor):
    float load_factor = (total_elements) / (bucket_count);
    当负载因子超过阈值(默认 1.0),容器会自动扩容并重新哈希(Rehash)。

4. 哈希表的性能分析

指标描述
时间复杂度- 平均:O(1)
- 最坏:O(n)(哈希冲突严重时)
空间复杂度O(n)(需额外空间存储桶和链表节点)
优点- 插入、查找、删除速度快。
- 支持动态扩容。
缺点- 哈希冲突影响性能。
- 不保证元素顺序。
- 内存占用较高。
性能优化技巧
  1. 选择好的哈希函数:减少冲突概率(如使用双哈希、混合哈希)。
  2. 调整负载因子:通过 max_load_factor 控制扩容频率。
    std::unordered_map<int, int> map;
    map.max_load_factor(0.7); // 负载因子不超过 70%
  3. 预分配桶数量:减少动态扩容次数。
    std::unordered_map<int, int> map(1000); // 预分配 1000 个桶

5. 哈希表的应用场景

场景推荐容器原因
字符串到整数的映射std::unordered_map快速查找字符串对应的值(如字典)
元素存在性判断std::unordered_setO(1) 时间复杂度判断元素是否存在
数据缓存std::unordered_map键值对缓存高频访问数据
布隆过滤器(Bloom Filter)自定义哈希表快速过滤不存在的数据(需配合位数组)

6. 哈希表 vs 其他数据结构

对比维度哈希表平衡二叉搜索树(如 std::map)​
查找速度平均 O(1),最坏 O(n)O(log n)
插入/删除速度平均 O(1),最坏 O(n)O(log n)
内存占用较高(桶和链表开销)较低(仅节点存储)
有序性无序有序(按键排序)
适用场景高频查询、去重需要有序遍历或范围查询

7. 常见问题与注意事项

  1. 哈希冲突攻击

    • 攻击者构造特定键使哈希表退化为链表,导致性能下降。
    • 解决方案:使用加密哈希函数(如 std::"crypto::sha256)或增加盐值(Salt)。
  2. 键的比较

    • 默认使用 operator== 和 std::hash,自定义类型需同时特化这两个函数。
    // 自定义类型 Person
    struct Person {std::string name;int age;bool operator==(const Person& other) const {return name == other.name && age == other.age;}
    };// 特化 std::hash
    namespace std {template<> struct hash<Person> {size_t operator()(const Person& p) const {return hash<string>()(p.name) ^ hash<int>()(p.age);}};
    }
  3. 性能瓶颈

    • 频繁的哈希冲突会导致性能下降,需通过优化哈希函数或增加桶数量解决。

8. 总结

C++ 哈希表(std::unordered_map/std::unordered_set)是高效的数据结构,适用于需要快速访问去重的场景。其核心在于哈希函数的设计和冲突处理策略,实际使用中需根据数据特点调整参数(如负载因子、桶数量)以优化性能。对于追求有序性的场景,建议使用 std::map(红黑树实现),而哈希表则在追求速度时更优。

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

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

相关文章

【Visio使用教程】

Visio使用教程 1. Visio 的基本介绍1.1 Visio 是什么&#xff1f;核心特点&#xff1a; 1.2 主要功能与应用场景典型用途&#xff1a;行业应用&#xff1a; 1.3 版本与兼容性1.4 Visio下载1.5 安装 2. Visio 的界面与基础操作2.1 界面布局详解2.2 创建新文档与模板选择2.3 形状…

缓存使用的具体场景有哪些?缓存的一致性问题如何解决?缓存使用常见问题有哪些?

缓存使用场景、一致性及常见问题解析 一、缓存的核心使用场景 1. 高频读、低频写场景 典型场景&#xff1a;商品详情页、新闻资讯、用户基本信息。特点&#xff1a;数据更新频率低&#xff0c;但访问量极高。策略&#xff1a; Cache-Aside&#xff08;旁路缓存&#xff09;&a…

谷歌 Gemini 2.0 Flash实测:1条指令自动出图+配故事!

今天看到很多人夸Gemini 2.0 Flash的能力很强。 强大的P图能力&#xff0c;改背景、换衣服、调整姿态、表情控制等等 其中最让人眼前一亮的是图文功能。 它不仅是理解图文&#xff0c;而是能根据文字描述创作出一整个的故事、步骤图文。 我上手试了一下&#xff0c;感觉效果…

雷电模拟器连接Android Studio步骤

打开雷电模拟器&#xff0c;点击桌面系统应用—>打开设置—>关于平板电脑→连续点击5次版本号&#xff0c;会出现开发者选项—->进入开发者选项—->勾选打开usb调试。 命令行提示符&#xff0c;进入雷电模拟器安装目录。然后执行 Plain Text adb.exe connect 127.0…

配置普通链接二维码规则 校验文件检查失败

配置普通链接二维码规则 校验文件检查失败 1.问题 2.解决思路&#xff1a; 直接访问地址&#xff0c;不跳转文本&#xff0c;感觉是nginx配置问题打开服务器nginx 域名默认走80端口&#xff0c;配置了指定的访问路径&#xff0c;命令行 nginx -t ,nginx -s reload,start ngin…

c语言经典基础编程题

c语言经典基础编程题 一、输出输出1.1温度输出1.2排齐数据1.3进制转换 二、选择分支2.1求最大值2.2成绩评定2.3分段函数求值2.4 利润计算2.5判断闰年2.6二次方程根 三、循环结构3.1倒数求和3.2最大数3.3判断素数3.4判断完全数3.5打印菱形&#x1f680;&#x1f680;&#x1f68…

java数据处理:Map<String, Object>、Map<String, List<Room>>、Map<String, Integer>

已知数据都存在WargameConfig.HallMap里。 一、Map<String, Integer> 需求:按照scenarioName进行分类,统计每种scenarioName下的Room对象有多少; 思路:统计一个名为WargameConfig.HallMap的集合中,每个不同场景名称(scenarioName)出现的次数。返回一个键值对映射…

安全的实现数据备份和恢复

&#x1f4d5;我是廖志伟&#xff0c;一名Java开发工程师、《Java项目实战——深入理解大型互联网企业通用技术》&#xff08;基础篇&#xff09;、&#xff08;进阶篇&#xff09;、&#xff08;架构篇&#xff09;清华大学出版社签约作家、Java领域优质创作者、CSDN博客专家、…

TCP网络协议

TCP粘包 1. TCP在接收数据时&#xff0c;多包数据粘在了一起 2. 原因&#xff1a; 1. TCP发送数据时&#xff0c;没有及时发走&#xff0c;会根据缓冲区数据的情况进行重新组包&#xff1b; 2. TCP接收方&#xff0c;没有及时读走缓冲区数据&#xff0c;导致缓冲区大量数…

ES6回顾:闭包->(优点:实现工厂函数、记忆化和异步实现)、(应用场景:Promise的then与catch的回调、async/await、柯里化函数)

闭包讲解 ES6回顾&#xff1a;闭包->(优点&#xff1a;实现工厂函数、记忆化和异步实现&#xff09;、&#xff08;应用场景&#xff1a;Promise的then与catch的回调、async/await、柯里化函数&#xff09; 以下是与 JavaScript 闭包相关的常见考点整理&#xff0c;结合 Pro…

OpenMCU(三):STM32F103 FreeRTOS移植

概述 本文主要描述了STM32F103移植FreeRTOS的简要步骤。移植描述过程中&#xff0c;忽略了Keil软件的部分使用技巧。默认读者熟练使用Keil软件。本文的描述是基于OpenMCU_RTOS这个工程&#xff0c;该工程已经下载放好了移植STM32F103 FreeRTOS的所有文件 OpenMCU_RTOS工程的愿景…

生成对抗网络(GAN)原理与应用

目录 一、引言 二、GAN的基本原理 &#xff08;一&#xff09;生成器&#xff08;Generator&#xff09;的工作机制 &#xff08;二&#xff09;判别器&#xff08;Discriminator&#xff09;的工作机制 &#xff08;三&#xff09;对抗训练的过程 三、GAN在AIGC生图中的应…

STM32 内置的通讯协议

数据是以帧为单位发的 USART和UART的区别就是有没有同步功能 同步是两端设备有时钟连接&#xff0c;异步是没时钟连接&#xff0c;靠约定号的频率&#xff08;波特率&#xff09;接收发送数据 RTS和CTS是用来给外界发送已“可接收”或“可发送”信号的&#xff0c;一般用不到…

ES 使用geo point 查询离目标地址最近的数据

需求描述&#xff1a;项目中需要通过经纬度坐标查询目标地所在的行政区。 解决思路大致有种&#xff0c;使用es和mysql分别查询。 1、使用es进行查询 将带有经纬度坐标的省市区数据存入es中&#xff0c;mappings字段使用geo point类型&#xff0c;索引及查询dsl如下。 geo p…

Appium等待机制--强制等待、隐式等待、显式等待

书接上回&#xff0c;Appium高级操作--其他操作-CSDN博客文章浏览阅读182次&#xff0c;点赞6次&#xff0c;收藏7次。书接上回Appium高级操作--从源码角度解析--模拟复杂手势操作-CSDN博客。https://blog.csdn.net/fantasy_4/article/details/146162851主要讲解了Appium的一些…

【架构艺术】Go语言微服务monorepo的代码架构设计

近期因为项目架构升级原因&#xff0c;笔者着手调研一些go项目monorepo的代码架构设计&#xff0c;目标是长期把既有微服务项目重要的部分都转移到monorepo上面&#xff0c;让代码更容易维护&#xff0c;协作开发更加方便。虽然经验不多&#xff0c;但既然有了初步的调研&#…

深入解析 JVM —— 从基础概念到实战调优的全链路学习指南

文章目录 一、为什么要学习 JVM&#xff1f;1. 面试必备与技能提升2. 性能优化与问题诊断3. 编写高质量代码 二、JVM 基础概念与体系结构1. JVM 简介2. JDK、JRE 与 JVM 三、JVM 内存模型1. 线程私有区2. 线程共享区 四、类加载机制与双亲委派1. 类加载过程2. 双亲委派模型3. 动…

前端及后端实现csv文件下载功能

方法一、 前端内容&#xff1a; const url window.URL.createObjectURL(new Blob([res.data])); const link document.createElement(a); link.href url; const fileNameDateTime getFormattedDateTime(); const filename "用户提现列表"fileNameDateTime.csv…

QT中委托QStyledItemDelegate的使用

目录 一、子类化委托 二、委托方法实现 1)createEditor 2)setEditorData 3)setModelData 4)updateEditorGeometry 三、委托使用 四、总结 Qt的数据容器控件采用模型/视图(model/view)架构设计。模型用于存放控件的数据,视图则用于显示编辑数据,而委托则是…

OpenCV实现视频背景提取

在计算机视觉领域&#xff0c;背景减除&#xff08;Background Subtraction&#xff09;是一种常用的技术&#xff0c;用于从视频序列中提取前景对象。 背景减除的核心思想是通过建模背景&#xff0c;然后将当前帧与背景模型进行比较&#xff0c;从而分离出前景对象。 OpenCV…