跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

C++ 哈希表原理与 STL 容器实现详解

哈希表作为 unordered_map 和 unordered_set 的底层结构,核心在于通过哈希函数建立键值映射。深入剖析了哈希冲突的解决策略,对比了闭散列(线性探测)与拉链法的优劣,重点阐述了开放定址法中“删除标记”的设计必要性及扩容时的重哈希逻辑。同时,针对非整型 Key 提供了仿函数特化方案,并详细拆解了迭代器的封装过程,包括如何支持 const 迭代器及类型转换,最终完成了 C++ 标准库风格的哈希容器实现。

未来可期发布于 2026/3/15更新于 2026/9/1049 浏览
C++ 哈希表原理与 STL 容器实现详解

关联式容器初探

首先回顾一下 unordered_map 和 unordered_set,它们基于哈希表实现,用法与红黑树版本的 map/set 类似。主要区别在于:迭代器是单向的,且遍历时数据无序。

unordered_map 示例

它支持去重,也有 multi 版本。unordered_map 同样遵循这一特性。

哈希的核心思想

哈希的本质是在存储的值与位置之间建立映射关系。相比暴力查找的低效和二分查找对有序性的依赖,哈希提供了 O(1) 的平均查找复杂度。

例如计数排序中,最小值 15 映射到索引 0,最大值 30 映射到对应位置。这种直接定址法适用于数据分布集中的场景。若数据分散,可采用除留余数法:i = key % 空间大小。

除留余数法

此时会面临哈希冲突问题,即不同 Key 映射到同一位置。解决冲突主要有两种方式:闭散列(开放定址法)和拉链法(哈希桶)。

冲突示意图

闭散列——开放定址法

当发生冲突时,闭散列会在表中寻找下一个可用位置。常见策略包括线性探测和二次探测。

以线性探测为例,若当前位置被占,则向后查找空位。若遍历完未找到,则绕回开头继续查找。

线性探测

删除操作的陷阱

直接删除元素会导致后续查找中断(遇到空位就停止)。因此需引入状态标记:EXIST(存在)、EMPTY(空)、DELETE(已删除)。查找时跳过 DELETE,直到遇到 EMPTY 才停止。

enum STATE { EXIST, EMPTY, DELETE };
template<class K, class V>
struct HashData {
    pair<K, V> _kv;
    STATE _state = EMPTY;
};

负载因子与扩容

哈希表不能无限填充,否则效率下降。通常设定负载因子阈值(如 0.7),超过则扩容。扩容时需重新计算所有元素的哈希位置(Rehash)。

if (_n * 10 / _table.size() >= 7) {
    size_t newSize = _table.size() * 2;
    HashTable<K, V, HashFunc> newHT;
    newHT._table.resize(newSize);
    for (size_t i = 0; i < _table.size(); i++) {
        if (_table[i]._state == EXIST) {
            newHT.Insert(_table[i]._kv);
        }
    }
    _table.swap(newHT._table);
}

Key 类型的泛型处理

默认哈希函数假设 Key 可取模。对于 string 等非整型,需使用仿函数特化。简单的字符串哈希可将字符 ASCII 累加,但易冲突。更优方案是结合乘法因子(如 131)进行混合。

template<> struct DefaultHashFunc<string> {
    size_t operator()(const string& str) {
        size_t hash = 0;
        for (auto ch : str) {
            hash *= 131;
            hash += ch;
        }
        return hash;
    }
};

拉链法——哈希桶

为避免探测带来的性能抖动,拉链法将每个桶设计为链表。冲突时直接在链表头插入新节点。

拉链法

扩容优化

扩容时,直接将旧链表的节点迁移到新表,避免重复构造节点。

// 迁移节点逻辑
for (size_t i = 0; i < _table.size(); i++) {
    Node* cur = _table[i];
    while (cur) {
        Node* next = cur->_next;
        size_t hashi = hf(kot(cur->_data)) % newSize;
        cur->_next = newTable[hashi];
        newTable[hashi] = cur;
        cur = next;
    }
    _table[i] = nullptr;
}

迭代器封装

为了兼容 STL 风格,需要手动实现迭代器。关键在于 operator++ 的处理:当前桶遍历完后,需查找下一个非空桶。

Self& operator++() {
    if (_node->_next) {
        _node = _node->_next;
    } else {
        // 查找下一个非空桶
        size_t hashi = hf(kot(_node->_data)) % _pht->_table.size();
        ++hashi;
        while (hashi < _pht->_table.size()) {
            if (_pht->_table[hashi]) {
                _node = _pht->_table[hashi];
                return *this;
            }
            ++hashi;
        }
        _node = nullptr;
    }
    return *this;
}

Const 迭代器支持

需注意 const 对象调用成员函数时权限限制。通过重载构造函数或调整成员变量访问权限,确保 const_iterator 能正确返回只读引用。

总结

通过上述步骤,我们实现了基于哈希表的 unordered_map 和 unordered_set 基础框架。理解其底层机制有助于在开发中更好地利用标准库容器,并在特定场景下定制高性能数据结构。

目录

  1. 关联式容器初探
  2. 哈希的核心思想
  3. 闭散列——开放定址法
  4. 拉链法——哈希桶
  5. 迭代器封装
  6. 总结

更多推荐文章

查看全部
  • MCPHost 实战:命令行驱动大模型与外部工具交互
  • 量化交易中 Python 与 C++ 的选择考量
  • 前端面试核心考点与高频题库汇总
  • Spring Boot 日志全方位指南:从入门到高级配置
  • C++ 高并发内存池实战:ThreadCache 设计与实现
  • 医学影像分类器:基于深度学习的肺结节检测
  • Web3D 技术栈指南:Blender、Three.js 与数字孪生开发
  • UNet WebUI 镜像 AI 抠图入门教程
  • 基于 DeepSeek 和 Cursor 构建智能代码审查工具实践
  • GitHub Copilot 提示词工程指南:从基础到精通的 AI 编程协作
  • Python Web UI 自动化测试:Jenkins 构建项目与定时执行
  • AI 大模型详解:定义、原理与核心应用
  • 腾讯云轻量应用服务器部署 OpenClaw 接入 QQ 与飞书机器人
  • 2025 最新大模型 LLM 学习路线图与岗位需求分析
  • 大模型、超大模型与 Foundation Model 概念解析
  • AI 效率工具实战:3 个普通人可掌握的工作技巧
  • 使用 VSCode 开发 STM32 全流程指南
  • Java 并发编程:JUC 包中原子操作类的原理和用法
  • OpenClaw 开源 AI 智能体部署指南:Discord 与微信接入配置
  • 使用 Trae 构建本地 AI 对话机器人

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online