当前位置: 首页 > news >正文

用c++写一个简单哈希表

什么是哈希表?
哈希表(Hash Table)是一种空间换时间的极致体现。
它的核心思想是映射。通过一个哈希函数,把数据的键(Key)直接转换成数组的下标。
理想情况:你想找数据,直接用函数算出下标,一步到位,时间复杂度直接降到 O(1)常数时间
现实挑战(哈希冲突):因为数组空间是有限的,不同的 Key 经过哈希函数计算后,可能会得到相同的下标。这就叫哈希冲突(Hash Collision)。如何解决冲突?
业界最经典、最常用的方案是链地址法(也叫拉链法)。
它的结构是:数组 + 链表。
数组的每个位置(称为“桶”)存放一个链表的头指针。如果多个 Key 算出了相同的下标,它们就会像火车车厢一样,挂在同一个桶的链表上。
C++ 实现:手写一个链式哈希表
下面我用 C++ 为你手搓一个支持自动扩容、素数桶优化的链式哈希表。为了让你能直接运行,我们以最基础的整数键值对为例:

#include <iostream> #include <vector> #include <list> #include <algorithm> // 用于 std::find using namespace std; class HashTable { private: vector<list<int>> table_; // 底层结构:数组 + 链表 size_t useBucketNum_; // 记录已使用的桶数量 double loadFactor_; // 装载因子阈值(行业标准 0.75) // 素数表:使用素数作为桶大小,能让哈希分布更均匀,减少冲突 const vector<int> primes_ = {53, 97, 193, 389, 769, 1543, 3079, 6151}; size_t primeIdx_ = 0; public: // 构造函数 HashTable(int size = 53, double loadFactor = 0.75) : useBucketNum_(0), loadFactor_(loadFactor) { // 选择大于等于用户指定大小的最小素数 for (; primeIdx_ < primes_.size(); ++primeIdx_) { if (primes_[primeIdx_] >= size) break; } if (primeIdx_ == primes_.size()) primeIdx_--; table_.resize(primes_[primeIdx_]); } // 插入元素 void insert(int key) { // 1. 检查装载因子,决定是否扩容 double factor = static_cast<double>(useBucketNum_) / table_.size(); if (factor > loadFactor_) expand(); // 2. 计算哈希下标 int idx = key % table_.size(); // 3. 检查是否已存在,避免重复插入 auto& bucket = table_[idx]; auto it = find(bucket.begin(), bucket.end(), key); if (it == bucket.end()) { if (bucket.empty()) useBucketNum_++; bucket.emplace_front(key); // 头插法,效率极高 O(1) } } // 查找元素 bool find(int key) { int idx = key % table_.size(); auto& bucket = table_[idx]; return find(bucket.begin(), bucket.end(), key) != bucket.end(); } // 删除元素 void erase(int key) { int idx = key % table_.size(); auto& bucket = table_[idx]; auto it = find(bucket.begin(), bucket.end(), key); if (it != bucket.end()) { bucket.erase(it); if (bucket.empty()) useBucketNum_--; } } private: // 扩容 + 重新哈希(核心) void expand() { if (primeIdx_ + 1 >= primes_.size()) return; // 已达最大容量 primeIdx_++; size_t newSize = primes_[primeIdx_]; vector<list<int>> oldTable; table_.swap(oldTable); // 高效交换,避免深拷贝开销 table_.resize(newSize); useBucketNum_ = 0; // 将旧数据重新哈希到新表 for (auto& bucket : oldTable) { for (int key : bucket) { insert(key); } } } }; // 简单测试 int main() { HashTable ht(10); ht.insert(10); ht.insert(63); // 63 % 53 = 10,与 10 冲突,挂在同一个链表 cout << "查找 63: " << (ht.find(63) ? "存在" : "不存在") << endl; ht.erase(10); cout << "删除后查找 10: " << (ht.find(10) ? "存在" : "不存在") << endl; return 0; }

代码中有以下模块:

1:自动扩容与素数表:当表中元素太多(装载因子超过 0.75)时,会自动扩容。并且扩容时,容量不是随便乘 2,而是取下一个素数。这在数学上能最大程度打散数据,减少哈希冲突。

2:头插法:发生冲突时,新元素直接插在链表头部(emplace_front),不需要遍历到尾部,插入操作依然是 O(1) 。
3:Swap 优化:扩容时,通过 swap 交换新旧表的底层指针,避免了内存深拷贝。

http://www.jsqmd.com/news/1325539/

相关文章:

  • 隐私计算:在不泄露数据前提下训练模型(296)
  • 2026嘉兴装修公司推荐:5家核验靠谱装企覆盖全需求,避开增项转包坑 - 甄选测评官
  • Unity游戏服务器高并发设计:基于select的多路复用架构与C++实现
  • 员工咨询扎堆刷屏,HR 深陷重复性答疑难以脱身
  • 2026佛山定制光伏支架成型机厂家怎么选?实用选购指南+联系方式 - mobible
  • 苏州吴中区汽修行业现状盘点:车主避坑指南与优质门店甄选 - 国麟测评
  • 抖音内容总结工具2026免费额度够用吗实测多款常用工具给出明确结论
  • 关于自动化测试数据驱动和关键字驱动的理解
  • 职称评审加分项全解析与材料准备实战技巧
  • 整数规划实战:从建模到求解,用Python+OR-Tools解决排班优化问题
  • 企业员工福利方案定制 节日福利礼品 一站式解决方案 - GrowUME
  • 西安交通大学 IFC 国际本科预科项目全解析:培养直通国外名校的留学预备人才 - 甄选测评官
  • 利用Spacedesk将平板变无线副屏:原理、部署与优化全指南
  • OpenHarmony与Flutter集成实现汉字拼音标注技术解析
  • Kubernetes GPU资源管理与Volcano批处理调度的工程实践
  • 2026年院线抗衰拓客产品订做厂家推荐:聚焦技术研发与留客实效 - 优质品牌商家
  • 枣庄市屋顶漏水怎么处理_2026鲁南淮河流域城市漏水维修流程教程与榜单 - 雨婺虹房屋维修
  • Redis Lua脚本实战:从原子性原理到高并发场景应用
  • COMSOL仿真谷霍尔效应光子晶体:从能带计算到单向传输验证
  • 2026年最新 挑选国内专业智慧园区公司的3个要点
  • PLC工业自动化控制:从基础原理到实战应用
  • UE5实时3D高斯渲染:从原理到工程实现全解析
  • 信奥赛C++二分图算法:从基础到实战应用
  • 2026大中型企业CRM选型指南:10款企业级系统推荐 - 纷享销客智能型CRM
  • 2026广州快消行业GEO优化公司甄选指南:实力服务商盘点 + 合作避坑FAQ - 产业观察报
  • 数据可视化入门:工具选择与设计原则
  • 2026湖州装修公司推荐:8家靠谱装企 多维度权威评级榜单 - 甄选测评官
  • KES 全文搜索与文本处理实战:文本检索、分词与高性能搜索
  • 2026最新5款AI编程工具深度实测推荐
  • 科源制药产品拟中选第十二批国家药品集采