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

数据结构(c++版):深入理解哈希表

哈希表是计算机科学中一种高效的数据结构,它能够在平均情况下以常数时间复杂度O(1)进行插入、删除和查找操作。让我们通过分析上面的代码,结合生动的比喻,来彻底理解哈希表的工作原理。

哈希表的基本概念

想象一下你去图书馆借书,图书馆有256个书架(对应哈希表的大小)。每本书都有一个唯一的ISBN号(键),你需要找到特定的书(值)。哈希表就是这样一个"智能图书馆系统"。

哈希节点:书的存放单元

template<typename Keytype,typename Valutype>
class HashNode
{
public:Keytype key;        // ISBN号(唯一标识)Valutype valu;      // 书的内容HashNode* next;     // 同一书架上下一本书的指针HashNode(const Keytype& key, const Valutype& valu){this->key = key;this->valu = valu;this->next = NULL;}
};

比喻​:每个HashNode就像图书馆中的一个书位,它记录了书的ISBN号(key)、书的内容(value),以及同一书架上下一本书的位置(next指针)。

哈希表主体:图书馆的智能管理系统

template<typename Keytype, typename Valutype>
class Hashtable
{
private:int size;                                   // 图书馆的书架总数HashNode<Keytype,Valutype>** table;        // 书架阵列(指针数组)int Hash(const Keytype& key)const          // 图书归类算法{int hashkey = key % size;              // 根据ISBN决定放在哪个书架if (hashkey < 0) hashkey += size;      // 确保书架编号为正数return hashkey;}

比喻​:

  • size:图书馆总共有的书架数量
  • table:所有书架的集合,每个书架可以放多本书
  • Hash函数:图书管理员的归类算法,根据ISBN号决定书应该放在哪个书架

哈希表的构建:建设图书馆

template<typename Keytype, typename Valutype>
Hashtable<Keytype, Valutype>::Hashtable(int size)
{this->size = size;this->table = new HashNode<Keytype, Valutype>* [size];for (int i = 0; i < size; i++){table[i] = NULL;  // 初始化所有书架为空}
}

比喻​:建设一个新图书馆,先确定要建多少个书架(size),然后准备好所有空书架(table数组初始化),确保每个书架初始时都是空的。

哈希函数:智能归类系统

哈希函数key % size就像图书管理员的归类规则:

  • 规则简单高效:直接取模运算
  • 均匀分布:确保书籍相对均匀地分布在不同书架
  • 快速定位:通过ISBN号能立即知道在哪个书架查找

插入操作:新书入库

template<typename Keytype, typename Valutype>
void Hashtable<Keytype, Valutype>::insert(const Keytype& key, const Valutype& valu)
{int index = Hash(key);  // 确定放在哪个书架HashNode<Keytype, Valutype>* now = new HashNode<Keytype, Valutype>(key, valu);if (table[index] == NULL)  // 如果书架是空的{table[index] = now;    // 直接放在书架最前面}else {  // 采用头插法now->next = table[index];table[index] = now;}
}

比喻流程​:

  1. 确定位置​:管理员用ISBN号计算这本书应该放在3号书架(index = 3)
  2. 准备书籍​:创建新的图书记录(new HashNode)
  3. 放置书籍​:
    • 如果3号书架是空的,直接放在最前面
    • 如果已有书籍,采用"新书放在最前面"的策略(头插法),这样新书更容易被找到

查找操作:借书查询

template<typename Keytype, typename Valutype>
bool Hashtable<Keytype, Valutype>::find(const Keytype& key,Valutype&valu)const
{int index = Hash(key);  // 先确定在哪个书架if (table[index])  // 如果这个书架有书{if (table[index]->key == key)  // 检查第一本书{valu = table[index]->valu;return true;}else {  // 继续检查书架上的其他书HashNode<Keytype, Valutype>* curr = table[index];while (curr->next && curr->next->key != key){curr = curr->next;}if (curr->next)  // 找到了{valu = curr->next->valu;return true;}}}return false;  // 整个图书馆都没有这本书
}

比喻流程​:

  1. 定位书架​:管理员根据ISBN算出在5号书架查找
  2. 顺序查找​:
    • 先看书架最前面的书(第一本)
    • 如果不是,继续查看同一书架上的下一本
    • 直到找到正确的ISBN或查完整个书架
  3. 返回结果​:找到则返回书的内容,否则报告书籍不存在

删除操作:书籍下架

template<typename Keytype, typename Valutype>
void Hashtable<Keytype, Valutype>::Dlete(const Keytype& key)
{int index = Hash(key);if (table[index])  // 如果这个书架有书{if (table[index]->key == key)  // 要删除的是第一本书{HashNode<Keytype, Valutype>* next = table[index]->next;delete table[index];table[index] = next;  // 第二本书成为新的第一本}else {HashNode<Keytype, Valutype>* curr = table[index];// 找到要删除书籍的前一本书while (curr->next && curr->next->key != key){curr = curr->next;}if (curr->next)  // 找到了要删除的书{HashNode<Keytype, Valutype>* next = curr->next->next;delete curr->next;     // 移除这本书curr->next = next;     // 前后书籍重新连接}}}
}

比喻流程​:

  1. 定位书架​:确定在哪个书架
  2. 查找书籍​:
    • 如果要删除的是书架的第一本书:直接移除,第二本书成为新的第一本
    • 如果要删除的是中间或后面的书:找到前一本书,让它直接指向后一本书,跳过要删除的书
  3. 清理空间​:物理上移除书籍记录

内存管理:图书馆的日常维护

template<typename Keytype, typename Valutype>
Hashtable<Keytype, Valutype>::~Hashtable()
{for (int i = 0; i < size; i++)  // 清理每个书架{if (table[i])  // 如果书架有书{HashNode<Keytype, Valutype>* curr = table[i];while (curr)  // 清空整个书架{HashNode<Keytype, Valutype>* next = curr->next;delete curr;  // 移除每本书curr = next;}}table[i] = NULL;  // 标记书架为空}delete []table;  // 拆除所有书架table = NULL;    // 标记图书馆已拆除
}

比喻​:图书馆关闭时的清理工作:

  1. 逐个书架清理​:从0号书架开始,清理每个书架上的所有书籍
  2. 书籍回收​:逐本书籍进行销毁(释放内存)
  3. 拆除设施​:最后拆除所有书架结构本身

哈希冲突与解决策略

哈希冲突就像不同ISBN的书籍被分配到同一个书架,这是不可避免的。我们的代码使用"链地址法"解决冲突:

  • 冲突发生​:两本不同的书被分配到同一个书架
  • 解决方案​:在同一个书架上用链表连接多本书
  • 查找代价​:最坏情况下需要遍历整个书架的书籍

代码中的实际演示

int main()
{Hashtable<int, char>h(1000);  // 建立有1000个书架的图书馆// 入库新书h.insert(1, 'a');      // ISBN=1的书,内容='a'h.insert(2, 'b');h.insert(3, 'c');h.insert(545674, 'd');h.insert(1001, 'g');char val;if (!h.find(51, val))  // 查找ISBN=51的书{cout << "51 not found" << endl;  // 未找到}if (h.find(545674, val))  // 查找ISBN=545674的书{cout << "545674 is found is :" << val << endl;  // 找到并输出内容}return 0;
}

哈希表的优势与局限

优势​:

  • 快速访问​:平均O(1)时间复杂度
  • 灵活扩容​:可根据需要调整大小
  • 键值对存储​:天然的映射关系

局限​:

  • 内存占用​:需要预分配空间
  • 哈希冲突​:最坏情况下性能退化
  • 无序性​:元素存储顺序不确定

总结

哈希表就像是一个高效的智能图书馆系统,通过哈希函数快速定位,通过链表解决冲突。理解哈希表的关键在于:

  1. 哈希函数是心脏​:决定数据的分布效率
  2. 冲突解决是大脑​:处理不可避免的"撞车"情况
  3. 内存管理是后勤​:确保资源的高效利用

通过这个生动的图书馆比喻,相信你已经对哈希表的工作原理有了深刻的理解。下次使用字典或映射数据结构时,你会想起这个智能的"图书馆系统"是如何高效工作的!

源码及运行:

#include<iostream>
#include<string>
#include<unordered_map>
using namespace std;
template<typename Keytype,typename Valutype>
class HashNode
{
public:Keytype key;Valutype valu;HashNode* next;HashNode(const Keytype& key, const Valutype& valu)//传的参数是键和值并且要加引用不能修改传入进来的参数{this->key = key;this->valu = valu;this->next = NULL;}
};
template<typename Keytype, typename Valutype>
class Hashtable
{
private:int size;HashNode<Keytype,Valutype>** table;//跟邻接表有点相似int Hash(const Keytype& key)const{int hashkey = key % size;if (hashkey < 0){hashkey += size;}return hashkey;}
public:Hashtable(int size = 256);~Hashtable();void insert(const Keytype& key, const Valutype& valu);//不希望传进来的键和值被修改void Dlete(const Keytype& key);//键和值是一个整体删除键值就自然删除了bool find(const Keytype& key,Valutype& valu)const;};template<typename Keytype, typename Valutype>
Hashtable<Keytype, Valutype>::Hashtable(int size)
{this->size = size;//改变成员变量给成员size初始化this->table = new HashNode<Keytype, Valutype>* [size];//申请一个大小为size的表for (int i = 0; i < size; i++)//把表头置空,不然就变成野指针了{table[i] = NULL;}}
template<typename Keytype, typename Valutype>
Hashtable<Keytype, Valutype>::~Hashtable()
{for (int i = 0; i < size; i++){if (table[i])//遍历所有表头{HashNode<Keytype, Valutype>* curr = table[i];//申请临时变量来删除表里的同一个键的不同元素while (curr)//{HashNode<Keytype, Valutype>* next = curr->next;delete curr;curr = next;}}table[i] = NULL;//置空防止变成野指针}delete []table;//删除所有表头table = NULL;//给申请数组置空防止变成指针
}
template<typename Keytype, typename Valutype>
void Hashtable<Keytype, Valutype>::insert(const Keytype& key, const Valutype& valu)//插入操作
{int index = Hash(key);//把下标变成键值HashNode<Keytype, Valutype>* now = new HashNode<Keytype, Valutype>(key, valu);//创建新的哈希表节点if (table[index] == NULL)//表头为空则直接把要插入的键置为表头{table[index] = now;}else {//头插法插入表中now->next = table[index];table[index] = now;}}
template<typename Keytype, typename Valutype>
void Hashtable<Keytype, Valutype>::Dlete(const Keytype& key)//键和值是一个整体删除键值就自然删除了
{int index = Hash(key);if (table[key])//哈希表头是否为空,为空就不用删除了{if (table[index] == key)//判断是否是表头{//是表头则将表头的下一个节点保存再next中,删除原来的表头,再把next置为表头HashNode<Keytype, Valutype>* next = table[index]->next;delete table[index];table[index] = next;}else {HashNode<Keytype, Valutype>* curr = table[key];//创建临时变量while (curr->next&&curr->next->key!=key)//遍历表头里面的元素找到要删除元素的前面一个{curr = curr->next;}if (curr->next)//创建临时遍历直接指向要删除的后面的节点,释放掉要删除节点的内存,把被删除的节点置为下一个节点。{HashNode<Keytype, Valutype>* next = curr->next->next;delete curr->next;curr->next = next;}}}
}
template<typename Keytype, typename Valutype>
bool Hashtable<Keytype, Valutype>::find(const Keytype& key,Valutype&valu)const//我传进来的valu会在找到后被修改所以valu前面不用const
{int index = Hash(key);//查找与删除部分逻辑相同,如果是表头就直接将值赋给传进来的键所对应的值然后返回。if (table[index]){if (table[index]->key == key){valu = table[index]->valu;return true;}else {HashNode<Keytype, Valutype>* curr = table[index];while (curr->next && curr->next->key != key){curr = curr->next;}if (curr->next){valu = curr->next->valu;return true;}}}return false;
}
int main()
{Hashtable<int, char>h(1000);h.insert(1, 'a');h.insert(2, 'b');h.insert(3, 'c');h.insert(545674, 'd');h.insert(1001, 'g');char val;if (!h.find(51, val)){cout << "51 not found" << endl;}if (h.find(545674, val)){cout << "545674 is found is :" << val << endl;}if (h.find(1001, val)){cout << "1001 is found is :" << val << endl;}Hashcount<long long>hc(1000);hc.add(6);hc.add(6);hc.add(6);hc.add(6);hc.add(5);hc.add(6);hc.add(6);hc.sub(6);cout << hc.get(6) << endl;cout << hc.get(5) << endl;return 0;
}

http://www.dtcms.com/a/577784.html

相关文章:

  • HIKVISION前端一面面经整理
  • Rocky9基于MySQL安装Zabbix7
  • 安庆网站制作1688阿里巴巴国际站首页
  • 阿里云微服务引擎 MSE 及 API 网关 2025 年 10 月产品动态
  • 太原网站建设内蒙古建设工程造价信息网官网中项网
  • Oracle 19C RAC下TRUNCATE TABLE的REUSE STORAGE选项作用和风险浅析!
  • CentOS 7 Oracle 11g RAC+DataGuard 分阶段静默部署脚本
  • 索牛网站建设江苏省建设厅官网网站首页
  • 三网合一网站系统晋城市网站建设
  • 智慧幼儿园管理系统-幼儿园多园区管理小程序的技术架构与应用实践:重构幼教领域数字化管理范式-幼儿园小程序开发-幼儿园软件开发-幼儿园系统开发定制
  • 精准招聘新纪元:AI 重构选才逻辑
  • 超聚变联手英特尔打造边缘智算一体机,重构工作站市场格局
  • 英国服务器Windows系统远程桌面安装与优化
  • 青岛做网站优化大屏网站模板
  • 多项分布 (Multinomial Distribution)
  • 网站gif横幅广告怎么做网站开发人员篡改客户数据
  • 大模型-vllm的知识点记录-1
  • 哪些网站是用织梦做的php做的直播网站
  • 为云原生加速:深入解析PoleFS分布式缓存系统BlobCache
  • xml方式实现AOP
  • XML签名
  • 云原生基石的试金石:基于 openEuler 部署 Docker 与 Nginx 的全景实录
  • 浏阳网站建设卷云网络南和网站seo
  • postgresql pg_upgrade源码阅读--doing
  • oracle导出 导入
  • 如何自己做个简单网站wordpress 中国提速
  • 程序安装包在ubuntu安装教程,以opencv安装为例
  • Linux 服务器内存监控与优化指南
  • APP应用怎么选择游戏盾
  • 医院网站建设联系方式为企业做一件小事