哈希表特性与unordered_map/unordered_set实现分析

目录

一、哈希表核心特性总结

1.开放地址法

2.链地址法

二、unordered_map/unordered_set实现要点分析

1. 哈希表核心实现(HashTable2.h)

(1) 哈希函数处理

(2) 链地址法实现

(3) 迭代器设计

(4) hashtable设计

2. unordered_map实现要点

3. unordered_map实现要点


一、哈希表核心特性总结

哈希表有两种表 : 一种是闭散列(开放地址法),一种是开散列(链地址法),我将用画图来带大家理解这两种方法的思路

1.开放地址法

线性探测

v

2.链地址法

二、unordered_map/unordered_set实现要点分析

1. 哈希表核心实现(HashTable2.h)

(1) 哈希函数处理

仅使用首字符会导致大量冲突(如所有以相同字母开头的字符串),使用BKDR哈希,通过累乘质数和字符值获得更好分布

// 默认哈希函数(直接类型转换)
template<class K>
struct DefaultHashFunc {size_t operator()(const K& key) {return (size_t)key;}
};// 字符串特化版本
template<>
struct DefaultHashFunc<string> {size_t operator()(const string& str) {size_t hash = 0;for(auto ch : str) {hash += 131;hash += ch;}return hash;}
};
(2) 链地址法实现
template<class T>
struct HashNode
{T _data;HashNode<T>* next;HashNode(const T& data):_data(data), next(nullptr){}
};
(3) 迭代器设计
//前置申明,告诉Iterator,申明了哈希表
template<class K, class T, class KeyOfT, class HashFunc >
class HashTable;template<class K, class T,class Ptr,class Ref, class KeyOfT, class HashFunc>
struct HTIterator
{typedef HashNode<T> Node; typedef HTIterator<K, T,Ptr,Ref, KeyOfT, HashFunc>  Self;//这是什么鬼??typedef HTIterator<K, T, T*, T&, KeyOfT, HashFunc>  Iterator;Node* _node;//就是不能改*phtconst HashTable<K, T, KeyOfT, HashFunc>* _pht;//为什么需要节点的指针和哈希的指针/*HTIterator(Node * node,HashTable<K, T, KeyOfT, HashFunc>* pht):_node(node),_pht(pht){}*///这个_pht加了const的重载HTIterator(Node* node,const  HashTable<K, T, KeyOfT, HashFunc>* pht):_node(node), _pht(pht){}//普通迭代器时,它是拷贝构造//const迭代器时,它是构造HTIterator(const Iterator & it):_node(it._node), _pht(it._pht){}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}Self& operator++(){if (_node->next){//当前桶还没完_node = _node->next;}else{KeyOfT kot;HashFunc hf;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);}else{//为空继续加 ++hashi;}}_node = nullptr;}return *this;}bool operator!=(const Self& s){return  _node != s._node;}bool operator==(const Self& s){return  _node == s._node;}
};
(4) hashtable设计
	template<class K, class T,class KeyOfT, class HashFunc = DefaultHashFunc<K>>class HashTable{typedef HashNode<T> Node;////友元声明,类模版需要把模版参数带上template<class K, class T,class Ptr,class Ref ,class KeyOfT, class HashFunc >friend struct HTIterator;		public:typedef HTIterator<K, T,T*,T&, KeyOfT, HashFunc>  iterator;typedef HTIterator<K, T,const T*,const  T&, KeyOfT, HashFunc>  const_iterator;iterator begin(){//找第一个桶for (size_t i =0 ;i < _table.size();i++){Node* cur = _table[i];if (cur){//这里为什么传this ???  航哥说这里this是哈希表的指针return iterator(cur, this);}}//没有找到return iterator(nullptr, this);}iterator end(){return iterator(nullptr,this);}const_iterator begin()const{//找第一个桶for (size_t i = 0;i < _table.size();i++){Node* cur = _table[i];if (cur){return const_iterator(cur, this);}}//没有找到return const_iterator(nullptr, this);}const_iterator end()const{return const_iterator(nullptr, this);}HashTable(){//先把size开到10,然后把剩余的位置另存为空指针_table.resize(10, nullptr);}~HashTable(){for (size_t i = 0;i < _table.size();i++){Node* cur = _table[i];while (cur){Node* next = cur->next;delete cur;//	free cur;cur = next;}//因为cur是野指针,如果不置空,那么他有可能还会指向原来的节点cur = nullptr;}}pair<iterator,bool> insert(const T& data){KeyOfT kot;HashFunc hf;iterator it = Find(kot(data));//在这里是证明有相同的内容if (it!=end()){return make_pair(it,false);}//负载因子到一就扩容if (_n == _table.size()){size_t newSize = _table.size() * 2;//创建新表HashTable<K,T,KeyOfT,HashFunc> newht;//这个需要开新节点,而且销毁也麻烦//for (size_t i = 0;i < _table.size();i++)//{//	//.......//	ht.insert();//}vector<Node*> newTable;newTable.resize(newSize, nullptr);//便利旧表,顺手牵羊,把节点签下来挂到新表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;}_table.swap(newTable);}size_t hashi = hf(kot(data)) % _table.size();//头插,这个没看懂Node* newnode = new Node(data);newnode->next = _table[hashi];_table[hashi] = newnode;++_n;return make_pair(iterator(newnode,this), true);}iterator Find(const K& key){HashFunc hf;KeyOfT kot;size_t hashi = hf(key) % _table.size();Node* cur = _table[hashi];while (cur){if (kot(cur->_data) == key){return iterator(cur,this);}cur = cur->next;}return iterator(nullptr,this);}void Print(){for (size_t i = 0;i < _table.size();i++){printf("[%d]->", i);Node* cur = _table[i];while (cur){cout << cur->_kv.first << "->" << cur->_kv.second << "->";cur = cur->next;}printf("NULL\n");}}bool Erase(const K& key){HashFunc hf;KeyOfT kot;size_t hashi = hf(key) % _table.size();Node* cur = _table[hashi];Node* prev = nullptr;while (cur){if (kot(cur->_data) == key){if (prev == nullptr){_table[hashi] = cur->next;}else{prev->next = cur->next;}delete cur;return true;}prev = cur;cur = cur->next;}--_n;return false;}private:vector<Node*> _table;//指针数组size_t _n = 0;};

2. unordered_map实现要点

template<class K,class V>
class  unordered_map
{struct MapKeyOfT{const K& operator()(const pair<K,V>& kv){return kv.first;}};
public:typedef typename hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT>::iterator  iterator;typedef typename hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT>::const_iterator  const_iterator;pair<iterator,bool> insert(const pair<K,V>& kv){return _ht.insert(kv);}iterator begin(){return _ht.begin();}iterator end(){return _ht.end();}const_iterator begin()const{return _ht.begin();}const_iterator end()const{return _ht.end();}V& operator[](const K& key){pair<iterator,bool> ret=_ht.insert(make_pair(key,V()));return ret.first->second;}
private://这种const的特殊属性,一般是自己设置hash_bucket::HashTable<K,pair<const K,V>,MapKeyOfT >  _ht;
};

3. unordered_map实现要点

template<class K>
class unordered_set
{struct SetKeyOfT{const K & operator()(const K & key){return key;}};public:typedef	typename hash_bucket::HashTable<K,K,SetKeyOfT>::const_iterator iterator;typedef	typename hash_bucket::HashTable<K, K, SetKeyOfT>::const_iterator const_iterator;iterator begin()const
{return _ht.begin();
}iterator end()const
{return _ht.end();
}
pair<iterator,bool> insert(const K& key)
{//这样写是错的,因为这里接受的是const_iterator,返回的是iteratorpair<hash_bucket::HashTable<K, K, SetKeyOfT>::iterator, bool> ret = _ht.insert(key);return make_pair(ret.first, ret.second);
}private:hash_bucket::HashTable<K, K,SetKeyOfT>  _ht;
};

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

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

相关文章

生产环境sudo配置详细指南

目录 1. 语法格式 2. 配置示例 3. 使用 /etc/sudoers.d/ 目录管理&#xff08;推荐&#xff09; 4. 基础配置&#xff1a;用户权限管理 4.1 ​​添加用户到sudo组 ​​4.2 验证用户组信息 5. sudo日志配置 5.1 修改sudoers配置文件 5.2 创建日志目录与权限设置 6. Su…

CSS动态视口单位:彻底解决移动端适配顽疾,告别布局跳动

你是否曾被这些问题困扰&#xff1a; 移动端页面滚动时&#xff0c;地址栏收缩导致页面高度突变&#xff0c;元素错位&#xff1f;100vh在移动设备上实际高度超出可视区域&#xff1f;全屏弹窗底部总被浏览器UI遮挡&#xff1f; 这些痛点背后都是传统视口单位的局限——无法响应…

【P27 4-8】OpenCV Python——Mat类、深拷贝(clone、copyTo、copy)、浅拷贝,原理讲解与示例代码

P27 4-8 1 Mat结构体2 深拷贝VS浅拷贝3 代码示例1 Mat结构体 2 深拷贝VS浅拷贝 只拷贝了头部&#xff0c;header&#xff0c;&#xff0c;但是data部分是共用的&#xff0c;速度非常快&#xff1b; 缺点&#xff0c;任意一个修改&#xff0c;另一个data跟着变&#xff0c;这就是…

容器运行时支持GPU,并使用1panel安装ollama

前言 安装Docker请看之前博文&#xff1a;Docker实战中1panel方式安装Docker。 安装 NVIDIA 容器工具包 https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html 安装 先决条件 阅读有关平台支持的部分。为您的 Linux 发行版安装…

高并发内存池 性能瓶颈分析与基数树优化(9)

文章目录前言一、性能瓶颈分析操作步骤及其环境配置分析性能瓶颈二、基数树优化单层基数树二层基数树三层基数树三、使用基数树来优化代码总结前言 到了最后一篇喽&#xff0c;嘻嘻&#xff01;   终于是要告一段落了&#xff0c;接下来我们将学什么呢&#xff0c;再说吧&…

C#面试题及详细答案120道(01-10)-- 基础语法与数据类型

《前后端面试题》专栏集合了前后端各个知识模块的面试题&#xff0c;包括html&#xff0c;javascript&#xff0c;css&#xff0c;vue&#xff0c;react&#xff0c;java&#xff0c;Openlayers&#xff0c;leaflet&#xff0c;cesium&#xff0c;mapboxGL&#xff0c;threejs&…

机器翻译:回译与低资源优化详解

文章目录一、机器翻译的瓶颈二、回译&#xff08;Back-Translation&#xff09;2.1 什么是回译&#xff1f;2.2 为什么回译有效&#xff1f;2.3 回译的缺点与挑战三、低资源优化详解3.1 数据层面策略3.2 模型层面策略3.3 架构层面策略四、回译与低资源优化对比4.1 回译与低资源…

leetcode-python-344反转字符串

题目&#xff1a; 编写一个函数&#xff0c;其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。 不要给另外的数组分配额外的空间&#xff0c;你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。 示例 1&#xff1a; 输入&#xff1a;s [“h”,“…

【Python】新手入门:什么是python字符编码?python标识符?什么是pyhon保留字?

🌈 个人主页:(时光煮雨) 🔥 高质量专栏:vulnhub靶机渗透测试 👈 希望得到您的订阅和支持~ 💡 创作高质量博文(平均质量分95+),分享更多关于网络安全、Python领域的优质内容!(希望得到您的关注~) 🌵文章目录🌵 前言 💡一、编码 📝二、标识符 🎯三、Py…

为什么要使用消息队列呢?

消息队列&#xff08;Message Queue&#xff0c;MQ&#xff09;在分布式系统中扮演着 ​异步通信枢纽​ 的角色&#xff0c;其核心价值在于解决系统间的解耦、流量削峰、异步处理等关键问题。以下是它的核心价值及典型应用场景&#xff1a;⚙️ 一、核心价值&#xff1a;解决什…

ROS机器人云实践案例博客建议和范文-AI版本

海报图AI图1AI图2zhangrelay的博客以技术深度、跨界思考和社会洞察为特色&#xff0c;内容兼具实用性与前瞻性&#xff0c;但部分观点存在争议&#xff0c;需结合具体主题辩证看待。以下从内容特色、技术深度、社会洞察、争议点四个维度展开分析&#xff1a;一、内容特色&#…

UE小:编辑器模式下「窗口/鼠标不在焦点」时仍保持高帧率

要在UE编辑器模式下「窗口/鼠标不在焦点」时仍保持高帧率&#xff0c;可按下面做法&#xff1a; 关闭编辑器的后台降频选项&#xff1a;在 Edit -> Editor Preferences -> General -> Performance 中取消勾选 “Use Less CPU when in Background”。

VS2022 + Qt 5.15.2+Occ开发环境搭建流程

Visual Studio 2022 Qt 5.15.2 图形处理开发环境搭建流程 1. 安装 Visual Studio 2022 下载安装程序&#xff1a;Visual Studio 官网选择工作负载&#xff1a; ✔️ “使用C的桌面开发”✔️ “通用Windows平台开发”&#xff08;可选&#xff09; 安装组件&#xff1a; ✔️…

多任务并发:进程管理的核心奥秘

多任务&#xff08;并发&#xff09;&#xff1a;让系统具备同时处理多个任务的能力1. 多进程2. 多线程3. 进程间通信一、进程的基本概念1. 什么是进程&#xff1f;正在运行的程序&#xff0c;其运行过程中需要消耗内存和CPU。进程的特点&#xff1a;动态性&#xff1a;进程是程…

高效TypeScript开发:VSCode终极配置指南

⚙️ VSCode TypeScript 专属效率设置大全 (纯 settings.json 配置) // .vscode/settings.json {/* &#x1f50d; 引用与类型追踪 */"typescript.referencesCodeLens.enabled": true, // 显示引用计数(点击查看所有引用处)"typescript.implementationsCod…

资本的自我否定:四重矛盾中的历史辩证法

资本自诞生以来&#xff0c;便以“增殖”为唯一使命&#xff0c;如同一个不知疲倦的扩张机器&#xff0c;在推动生产力飞跃的同时&#xff0c;也埋下了自我毁灭的种子。这种自我否定并非外部力量的强加&#xff0c;而是其内在逻辑的必然展开——从价格战的困局到经济危机的周期…

Linux系统安装Docker及常见问题解决

1.1 解决安装Docker问题 Linux的发行版本&#xff0c;大多数还是在用CentOS&#xff0c;虽然CentOS已经不更新了。。。。。CentOS因为不更新了&#xff0c;所以很多的yum源都失效了。导致安装Docker失败&#xff01; 只需要更新一下yum源。直接将之前默认的yum源替换为阿里的…

CICD-Devops整合Kubernetes-4

Devops整合Kubernetes Kubernetes部署快速安装Kubernetes **官网&#xff1a;**https://kuboard.cn/选择默认支持docker的版本1.19前置环境部署 所有节点均需执行同操作 # 配置主机名解析 [rootKubernetes-master ~]# echo "127.0.0.1 $(hostname)" >> /etc/ho…

C/C++ 指针与内存操作详解——从一级指针到字符串转换函数的完整解析

C/C 指针与内存操作详解——从一级指针到字符串转换函数的完整解析 本文将带你系统理解 一级指针与二级指针的区别、数组拷贝的注意事项、字符串转整数函数实现 等 C/C 编程中常见且易混淆的知识点&#xff0c;并配合详细代码示例与常见坑点分析&#xff0c;让你从入门到掌握。…

Java -- HashSet的全面说明-Map接口的常用方法-遍历方法

目录 1. HashSet的全面说明 2. Map接口实现类的特点 注意&#xff1a;讲的是JDK8的Map接口特点 3. Map接口的常用方法 4. Map遍历方法 1. HashSet的全面说明 1. HashSet实现了Set接口 2. HashSet实际上是HashMap 3. 可以存放null值&#xff0c;但是只能有一个null 4. H…