【C++】哈希表:简单易懂的核心讲解(含实战用法)

哈希表(Hash Table)是 C++ 中高效的键值对(key-value)存储结构,核心优势是 插入、查找、删除操作的平均时间复杂度接近 O (1)—— 比数组查找(O (n))、有序容器(如 map,O (log n))快得多,日常开发中常用来解决 “快速查找 / 去重 / 统计” 类问题。

一、先搞懂:哈希表到底是什么?(大白话版)

你可以把哈希表想象成一个 “带编号的快递柜”

  • key(键):快递的 “取件码”(唯一标识);
  • value(值):快递本身(要存储的数据);
  • 哈希函数:把 “取件码”(key)转换成 “柜子编号”(数组索引)的规则;
  • 数组 + 链表 / 红黑树:柜子的存储结构(数组是主体,链表 / 红黑树解决 “多个取件码对应同一个柜子” 的冲突问题)。

举个例子:要存储 “学生学号→学生姓名”,哈希函数可以是 “学号 mod 100”—— 学号 202501 对应柜子 1,学号 202502 对应柜子 2,直接按编号找柜子,不用逐个遍历,这就是 O (1) 效率的核心。

二、C++ 中怎么用哈希表?(重点:STL 容器)

C++ 标准库没有直接叫 “HashTable” 的类,但提供了 2 个核心哈希容器,直接用就行:

容器特点(核心区别)适用场景
unordered_map存储 key-value 对,key 唯一,无序快速查找 / 映射(如字典、缓存)
unordered_set只存储 key,key 唯一,无序快速去重 / 判断元素是否存在

补充:和有序容器的区别

  • map/set 是有序的(基于红黑树),查找效率 O (log n);
  • unordered_map/unordered_set 是无序的(基于哈希表),平均 O (1),但最坏 O (n)(冲突严重时),且内存占用略高。

三、实战代码:3 个最常用场景

场景 1:用 unordered_map 做 “键值映射”(如字典 / 缓存)

比如存储 “单词→释义”,快速根据单词查意思:

cpp

运行

#include <iostream> #include <unordered_map> #include <string> using namespace std; int main() { // 1. 定义哈希表:key=string(单词),value=string(释义) unordered_map<string, string> dict; // 2. 插入元素(3种方式) dict["apple"] = "苹果"; // 直接赋值(最简洁) dict.insert({"banana", "香蕉"}); // insert 函数(键不存在时插入) dict.emplace("orange", "橙子"); // emplace(效率更高,直接构造对象) // 3. 查找元素(核心:快速查找) string key = "apple"; if (dict.find(key) != dict.end()) { // find 返回迭代器,找不到返回 end() cout << key << " → " << dict[key] << endl; // 输出:apple → 苹果 } else { cout << "未找到该单词" << endl; } // 4. 遍历哈希表(无序) for (auto& pair : dict) { // pair.first 是 key,pair.second 是 value cout << pair.first << " : " << pair.second << endl; } // 5. 删除元素 dict.erase("banana"); // 按 key 删除 return 0; } 

场景 2:用 unordered_set 去重 / 判断存在

比如给一个数组去重,或快速判断某个数是否在数组中:

cpp

运行

#include <iostream> #include <unordered_set> #include <vector> using namespace std; int main() { vector<int> nums = {1, 2, 2, 3, 3, 3, 4}; unordered_set<int> unique_nums; // 去重:插入时自动忽略重复 key for (int num : nums) { unique_nums.insert(num); // 最终只存 1,2,3,4 } // 判断元素是否存在 int target = 3; if (unique_nums.count(target)) { // count 返回 0 或 1(key 唯一) cout << target << " 存在" << endl; // 输出:3 存在 } // 遍历去重后的结果 for (int num : unique_nums) { cout << num << " "; // 输出:1 2 3 4(顺序不确定) } return 0; } 

场景 3:用哈希表统计频率(如计数问题)

比如统计字符串中每个字符出现的次数:

cpp

运行

#include <iostream> #include <unordered_map> #include <string> using namespace std; int main() { string s = "hello world"; unordered_map<char, int> freq; // key=字符,value=出现次数 // 统计频率 for (char c : s) { freq[c]++; // 不存在的 key 会自动初始化为 0,再 ++ } // 输出结果 for (auto& pair : freq) { cout << "'" << pair.first << "' : " << pair.second << "次" << endl; } // 部分输出:'h':1次, 'e':1次, 'l':3次, 'o':2次... return 0; } 

四、关键注意点(避坑指南)

  1. key 必须可哈希:C++ 中默认支持的 key 类型:intstringdouble 等基础类型;自定义类型(如结构体)作为 key 时,需要手动重载 == 运算符(判断键是否相等)和提供哈希函数(否则编译器报错)。
  2. 无序性unordered_* 容器的遍历顺序和插入顺序无关,若需要有序,应改用 map/set
  3. 冲突问题:哈希表的效率依赖 “哈希函数的好坏”—— 好的哈希函数能减少 “多个 key 对应同一个索引” 的冲突;STL 已内置优化,日常开发不用手动处理冲突。
  4. 性能权衡:追求极致查找速度、不关心顺序 → 用 unordered_map/unordered_set;需要有序存储、允许略低效率 → 用 map/set

总结

哈希表是 C++ 中 “以空间换时间” 的典范,核心价值是 O (1) 级别的查找 / 插入 / 删除。日常开发中,只要遇到 “键值映射、去重、频率统计” 类问题,优先用 unordered_map 或 unordered_set,简单直接又高效!

Read more

从vw/vh到clamp(),前端响应式设计的痛点与进化

从vw/vh到clamp(),前端响应式设计的痛点与进化

目录 从vw/vh到clamp(),前端响应式设计的痛点与进化 一、原生响应式设计的痛点 1、使用 vw/vh/% 的蜜月期与矛盾点 2、以 px+@media 为主轴实现多端样式兼容 二、clamp():响应式设计的新思路 1、clamp() 是什么? 2、优势分析 三、实际应用场景示例 1、标题文字大小 2、布局容器宽度 3、按钮与间距 4、配合calc()实现更灵活布局 四、clamp() 的局限与思考 五、结语 从vw/vh到clamp(),前端响应式设计的痛点与进化 一、原生响应式设计的痛点 1、使用 vw/vh/% 的蜜月期与矛盾点

By Ne0inhk

Flutter 三方库 jwt_io 的鸿蒙化适配指南 - 在鸿蒙系统上构建极致、严谨、全能的 JSON Web Token (JWT) 加解密与身份安全验证引擎

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 jwt_io 的鸿蒙化适配指南 - 在鸿蒙系统上构建极致、严谨、全能的 JSON Web Token (JWT) 加解密与身份安全验证引擎 在鸿蒙(OpenHarmony)系统的端云一体化登录、政企应用的安全审计或复杂的跨端权限校验场景中,如何确保来自云端授信中心的 JWT Token 既能被正确解析(Decode),又能被严密地校验其合法性与过期时间?jwt_io 为开发者提供了一套工业级的、基于 RFC 7519 标准的 JSON Web Token 深度处理方案。本文将深入实战其在鸿蒙应用安全底座中的应用。 前言 什么是 JWT IO?它不仅是一个简单的 Base64 解码器,而是一个具备深厚 RFC

By Ne0inhk
分享一个开箱即用的 React K 线图组件,前端炒股看盘必备

分享一个开箱即用的 React K 线图组件,前端炒股看盘必备

一个 prop 画出专业 K 线图,数据获取和指标计算全自动。 为什么又造了个轮子 先说结论:不是我想造,是被逼的。 需求很简单 —— 在一个 React 项目里加一个股票 K 线图,要能切周期、看指标、支持缩放拖拽。听起来是不是很基础? 然后我开始找现成方案。TradingView 的 Lightweight Charts 不错,但免费版功能有限,而且它不是 React 组件,得自己封装一层。npm 上搜 “react kline” 或者 “react candlestick”,出来的结果要么年久失修,要么只是个 demo 级别的东西,拿来用还不如自己写。 既然找不到趁手的,那就自己搞一个。顺便把它做得通用一点,发出来给大家省点时间。 长什么样 项目名叫 kline-charts-react,

By Ne0inhk
回看经典!第十三章 C语言数据结构与算法基础:文件操作、排序查找实现及链表简介(2015年C语言培训班笔记重读)

回看经典!第十三章 C语言数据结构与算法基础:文件操作、排序查找实现及链表简介(2015年C语言培训班笔记重读)

目录 第十三章 基础数据结构 第1课:复习文件操作 第2课:冒泡排序与选择排序 第3课:二分查找算法 第4课:用递归实现二分查找 第5课:单向链表的实现         本文汇总了C语言在数据结构入门阶段的多个核心主题。包括文件操作(fopen、读写、指针)、基础排序算法(冒泡、选择)与查找算法(顺序、二分查找及其递归实现)的原理与代码实现,并简要介绍了单向链表的存储特点。通过对比和多个代码示例,为理解更复杂的数据结构与算法打下坚实基础。 第十三章 基础数据结构 第1课:复习文件操作 fopen函数的参数中,没有写具体路径,则表示在程序运行的当前目录下(相对路径);写了具体路径就是绝对路径。 文件结尾标识符EOF的使用 案例1:用feof判断读取下面文件中一个个字符: 代码: int main(){        FILE *p=fopen("d:\\c1\\gcc\

By Ne0inhk