【C++/STL】:哈希表的改造 -- 封装unordered系列

目录

  • 前言
  • 一,哈希表的改造
    • 1. 哈希表的基本框架
    • 2. 对哈希桶节点结构的改造
    • 3. 哈希表的迭代器
      • 3.1 迭代器类
      • 3.2 Begin() 和 End()
  • 四,哈希表相关接口的改造
    • 4.1 Find 函数的改造
    • 4.2 Insert 函数的改造
  • 五,哈希表改造的完整代码
  • 六,unordered_set 的封装实现
  • 七,unordered_map 的封装实现

点击跳转至文章: 【C++/STL】:哈希 – 线性探测&哈希桶

前言

与map/set的封装类似,unordered系列的底层本质上也是复用,通过对哈希表的改造,再分别套上一层 unordered_map 和 unordered_set 的 “壳子”,以达到 “一表二用” 的目的。

各个结构的改造不再详细说明,细节可参考文章:map和set的封装

unordered系列的底层哈希表是用哈希桶结构实现的。

一,哈希表的改造

1. 哈希表的基本框架

(1) K:关键码类型;

(2) V::不同容器V的类型不同,如果unordered_map,V代表一个键值对,如果是unordered_set,V 为 K;

(3) KeyOfValue:因为V的类型不同,通过value取key的方式就不同,详细见unordered_map/set的实现;

(4) Hash:哈希函数仿函数对象类型,哈希函数使用除留余数法,需要将Key转换为整形数字才能取模。

template <class K, class T, class KeyOfT,class Hash = HashFunc<K>>
class HashBucket
{//友元template <class K, class T, class Ref, class Ptr,class KeyOfT, class Hash >friend struct HTIterator;typedef BucketNode<T> Node;
public:typedef HTIterator<K, T, T&, T*, KeyOfT, Hash> Iterator;typedef HTIterator<K, T, const T&, const T*, KeyOfT, Hash> ConstIterator;//其他核心操作……private:vector<Node*> _tables;  //指针数组size_t _n = 0;          //表中数据的个数
};
}

2. 对哈希桶节点结构的改造

template <class T>
struct BucketNode
{BucketNode<T>* _next;T _data;BucketNode(const T& data):_data(data), _next(nullptr){}
};

3. 哈希表的迭代器

3.1 迭代器类

(1) 这里的迭代器需要:构造节点指针哈希表对象指针

(2) 这里的迭代器类需要用到哈希表类的结构,相互依存,要用前置声明

//前置声明
template <class K, class T, class KeyOfT, class Hash >
class HashBucket;template <class K, class T, class Ref, class Ptr,class KeyOfT, class Hash >
struct HTIterator
{//需要节点指针,哈希表对象指针typedef BucketNode<T> Node;typedef HTIterator<K, T, Ref, Ptr, KeyOfT, Hash> Self;const HashBucket<K, T, KeyOfT, Hash>* _pht;Node* _node;HTIterator(Node* node, const HashBucket<K, T, KeyOfT, Hash>* pht):_node(node),_pht(pht){}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}bool operator!=(const Self& s){return _node != s._node;}Self& operator++(){if (_node->_next){_node = _node->_next;}else{KeyOfT kot;Hash hs;size_t hashi = hs(kot(_node->_data)) % _pht->_tables.size();hashi++;while (hashi < _pht->_tables.size()){if (_pht->_tables[hashi])break;hashi++;}if (hashi == _pht->_tables.size()) //走完了_node = nullptr; //End()else_node = _pht->_tables[hashi];}return *this;}
};

3.2 Begin() 和 End()

Iterator Begin()
{if (_n == 0)return End();for (size_t i = 0; i < _tables.size(); i++){Node* cur = _tables[i];if (cur)return Iterator(cur, this);}return End();
}Iterator End()
{return Iterator(nullptr, this);
}ConstIterator Begin()const
{if (_n == 0)return End();for (size_t i = 0; i < _tables.size(); i++){Node* cur = _tables[i];if (cur)return ConstIterator(cur, this);}return End();
}ConstIterator End()const
{return ConstIterator(nullptr, this);
}

四,哈希表相关接口的改造

4.1 Find 函数的改造

Iterator Find(const K& key)
{Hash hs;KeyOfT kot;size_t hashi = hs(key) % _tables.size();Node* cur = _tables[hashi];while (cur){if (kot(cur->_data) == key)return Iterator(cur, this);elsecur = cur->_next;}return End();
}

4.2 Insert 函数的改造

pair<Iterator, bool> Insert(const T& data)
{Hash hs;KeyOfT kot;Iterator it = Find(kot(data));if (it != End())return make_pair(it, false);//扩容if (_n == _tables.size()){vector<Node*> newTables;newTables.resize(_tables.size() * 2, nullptr);//把旧表的节点挪到新表for (size_t i = 0; i < _tables.size(); i++){Node* cur = _tables[i];while (cur){size_t hashi = hs(kot(cur->_data)) % newTables.size();Node* newnode = new Node(cur->_data);//头插newnode->_next = newTables[hashi];newTables[hashi] = newnode;cur = cur->_next;}_tables[i] = nullptr;}_tables.swap(newTables);}size_t hashi = hs(kot(data)) % _tables.size();Node* newnode = new Node(data);//头插newnode->_next = _tables[hashi];_tables[hashi] = newnode;_n++;return make_pair(Iterator(newnode, this), true);
}

五,哈希表改造的完整代码

HashTable.h

template<class K>
struct HashFunc
{size_t operator()(const K& key){return (size_t)key;}
};//对string类型的特化
template<>
struct HashFunc<string>
{size_t operator()(const string& s){size_t n = 0;for (auto ch : s){n += ch;n *= 31;}return n;}
};template <class T>
struct BucketNode
{BucketNode<T>* _next;T _data;BucketNode(const T& data):_data(data), _next(nullptr){}
};//前置声明
template <class K, class T, class KeyOfT, class Hash >
class HashBucket;template <class K, class T, class Ref, class Ptr,class KeyOfT, class Hash >
struct HTIterator
{//需要节点指针,哈希表对象指针typedef BucketNode<T> Node;//typedef HTIterator<K, T, KeyOfT, Hash> Self;typedef HTIterator<K, T, Ref, Ptr, KeyOfT, Hash> Self;const HashBucket<K, T, KeyOfT, Hash>* _pht;Node* _node;HTIterator(Node* node, const HashBucket<K, T, KeyOfT, Hash>* pht):_node(node),_pht(pht){}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}bool operator!=(const Self& s){return _node != s._node;}Self& operator++(){if (_node->_next){_node = _node->_next;}else{KeyOfT kot;Hash hs;size_t hashi = hs(kot(_node->_data)) % _pht->_tables.size();hashi++;while (hashi < _pht->_tables.size()){if (_pht->_tables[hashi])break;hashi++;}if (hashi == _pht->_tables.size()) //走完了_node = nullptr; //End()else_node = _pht->_tables[hashi];}return *this;}
};template <class K, class T, class KeyOfT,class Hash = HashFunc<K>>
class HashBucket
{//友元template <class K, class T, class Ref, class Ptr,class KeyOfT, class Hash >friend struct HTIterator;typedef BucketNode<T> Node;
public:typedef HTIterator<K, T, T&, T*, KeyOfT, Hash> Iterator;typedef HTIterator<K, T, const T&, const T*, KeyOfT, Hash> ConstIterator;Iterator Begin(){if (_n == 0)return End();for (size_t i = 0; i < _tables.size(); i++){Node* cur = _tables[i];if (cur)return Iterator(cur, this);}return End();}Iterator End(){return Iterator(nullptr, this);}ConstIterator Begin()const{if (_n == 0)return End();for (size_t i = 0; i < _tables.size(); i++){Node* cur = _tables[i];if (cur)return ConstIterator(cur, this);}return End();}ConstIterator End()const{return ConstIterator(nullptr, this);}HashBucket(){_tables.resize(10, nullptr);}//依次把每个桶释放~HashBucket(){for (size_t i = 0; i < _tables.size(); i++){Node* cur = _tables[i];while (cur){Node* next = cur->_next;delete cur;cur = next;}_tables[i] = nullptr;}}pair<Iterator, bool> Insert(const T& data){Hash hs;KeyOfT kot;Iterator it = Find(kot(data));if (it != End())return make_pair(it, false);//扩容if (_n == _tables.size()){vector<Node*> newTables;newTables.resize(_tables.size() * 2, nullptr);//把旧表的节点挪到新表for (size_t i = 0; i < _tables.size(); i++){Node* cur = _tables[i];while (cur){size_t hashi = hs(kot(cur->_data)) % newTables.size();Node* newnode = new Node(cur->_data);//头插newnode->_next = newTables[hashi];newTables[hashi] = newnode;cur = cur->_next;}_tables[i] = nullptr;}_tables.swap(newTables);}size_t hashi = hs(kot(data)) % _tables.size();Node* newnode = new Node(data);//头插newnode->_next = _tables[hashi];_tables[hashi] = newnode;_n++;return make_pair(Iterator(newnode, this), true);}Iterator Find(const K& key){Hash hs;KeyOfT kot;size_t hashi = hs(key) % _tables.size();Node* cur = _tables[hashi];while (cur){if (kot(cur->_data) == key)return Iterator(cur, this);elsecur = cur->_next;}return End();}bool Erase(const T& data){Hash hs;KeyOfT kot;size_t hashi = hs(kot(data)) % _tables.size();Node* cur = _tables[hashi];Node* prev = nullptr;while (cur){if (cur->_data == data){//第一个节点if (prev == nullptr){_tables[hashi] = nullptr;}else{//在节点中间prev->_next = cur->_next;}delete cur;_n--;return true;}prev = cur;cur = cur->_next;}return false;}
private:vector<Node*> _tables;  //指针数组size_t _n = 0;          //表中数据的个数
};

六,unordered_set 的封装实现

unordered_set 的底层为哈希表,因此只需在unordered_set 内部封装哈希表,即可将该容器实现出来

unordered_set .h

#include "HashTable.h"namespace bit
{template <class K, class Hash = HashFunc<K>>class unordered_set{struct SetOfT{const K& operator()(const K& key){return key;}};public:typedef typename HashBucket<K, const K, SetOfT, Hash>::Iterator iterator;typedef typename HashBucket<K, const K, SetOfT, Hash>::ConstIterator const_iterator;iterator begin(){return ht.Begin();}iterator end(){return ht.End();}const_iterator begin()const{return ht.Begin();}const_iterator end()const{return ht.End();}pair<iterator, bool> insert(const K& key){return ht.Insert(key);}private:bit::HashBucket<K,const K, SetOfT, Hash> ht;};void Print(const unordered_set<int>& s){unordered_set<int>::const_iterator it = s.begin();while (it != s.end()){// *it += 1;cout << *it << " ";++it;}cout << endl;}void Test_set(){//int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };int a[] = { 16,3,7,11,9,26,18,14,15 };unordered_set<int> s;for (auto e : a)s.insert(e);for (auto e : s)cout << e << " ";cout << endl;Print(s);}
}

七,unordered_map 的封装实现

unordered_map 的底层结构就是哈希表,因此在map中直接封装一个哈希表,然后将其接口包装下即可

unordered_map.h

#include "HashTable.h"namespace bit
{template <class K, class V, class Hash = HashFunc<K>>class unordered_map{struct MapOfT{const K& operator()(const pair<K, V>& kv){return kv.first;}};public:typedef typename HashBucket<K, pair<const K, V>, MapOfT, Hash>::Iterator iterator;typedef typename HashBucket<K, pair<const K, V>, MapOfT, Hash>::ConstIterator const_iterator;iterator begin(){return ht.Begin();}iterator end(){return ht.End();}const_iterator begin()const{return ht.Begin();}const_iterator end()const{return ht.End();}pair<iterator, bool> insert(const pair<K,V>& kv){return ht.Insert(kv);}V& operator[](const K& key){pair<iterator, bool> ret = ht.Insert(make_pair(key, V()));return ret.first->second;}private:bit::HashBucket<K, pair<const K, V>, MapOfT, Hash> ht;};void Test_map(){unordered_map<string, string> dict;dict.insert({ "left","左边" });dict.insert({ "right","右边" });dict.insert({ "insert","插入" });dict["left"] = "剩余,左边";bit::unordered_map<string, string>::iterator it = dict.begin();while (it != dict.end()){//it->first += 'x'; //errit->second += 'y'; //okcout << it->first << ":" << it->second << endl;++it;}cout << endl;}
}

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

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

相关文章

【MATLAB源码】机器视觉与图像识别技术实战示例文档---鱼苗面积预测计数

系列文章目录 第一篇文章&#xff1a;【MATLAB源码】机器视觉与图像识别技术—视觉系统的构成(视频与图像格式转换代码及软件下载) 第二篇文章&#xff1a;【MATLAB源码】机器视觉与图像识别技术(2)—图像分割基础 第三篇文章&#xff1a;【MATLAB源码】机器视觉与图像识别技术…

提交高通量测序处理数据到 GEO --- 操作流程

❝ 写在前面 由于最近在提交课题数据到 NCBI 数据库&#xff0c;整理了相关笔记。本着自己学习、分享他人的态度&#xff0c;分享学习笔记&#xff0c;希望能对大家有所帮助。推荐先按顺序阅读往期内容&#xff1a; 1. 提交高通量测序数据到 GEO --- 说明书 2. 提交高通量测序原…

jQuery前端网页制作

1、Jquery的概述 1.1JavaScript库 JavaScript 高级程序设计(特别是对浏览器差异的复杂处理),通常很困难也很耗时。 为了应对这些调整,许多的 JavaScript (helper) 库应运而生。 这些 JavaScript 库常被称为 JavaScript 框架。 市面上一些广受欢迎的 JavaScript 框架:…

基于Docker搭建ELK

目录 1.系统操作 2.搭建es 3.kibana(新起终端跟es一起启动) 4.logstash&#xff08;新起终端和es一起启动&#xff09; 5.修改logstash配置文件 6. 创建索引 7. exit #退出容器 8. 在logstash节点插入数据&#xff0c;测试是否能拿取到&#xff08;下面如果本身有数据…

基于多种机器学习的豆瓣电影评分预测与多维度可视化【可加系统】

有需要本项目的代码或文档以及全部资源&#xff0c;或者部署调试可以私信博主 在本研究中&#xff0c;我们采用Python编程语言&#xff0c;利用爬虫技术实时获取豆瓣电影最新数据。通过分析豆瓣网站的结构&#xff0c;我们设计了一套有效的策略来爬取电影相关的JSON格式数据。…

[FBCTF2019]RCEService (PCRE回溯绕过和%a0换行绕过)

json格式输入ls出现index.php 这道题原本是给了源码的&#xff0c;BUUCTF没给 源码&#xff1a; <?phpputenv(PATH/home/rceservice/jail);if (isset($_REQUEST[cmd])) {$json $_REQUEST[cmd];if (!is_string($json)) {echo Hacking attempt detected<br/><br/…

ElasticSearch学习篇15_《检索技术核心20讲》进阶篇之TopK检索

背景 学习极客实践课程《检索技术核心20讲》https://time.geekbang.org/column/article/215243&#xff0c;文档形式记录笔记。 相关问题&#xff1a; ES全文检索是如何进行相关性打分的&#xff1f;ES中计算相关性得分的时机?如何加速TopK检索&#xff1f;三种思路 精准To…

eclipse ui bug

eclipse ui bug界面缺陷&#xff0c;可能项目过多&#xff0c;特别maven项目过多&#xff0c;下载&#xff0c;自动编译&#xff0c;加载更新界面异常 所有窗口死活Restore不回去了 1&#xff09;尝试创建项目&#xff0c;还原界面&#xff0c;失败 2&#xff09;关闭所有窗口&…

Python写UI自动化--playwright(pytest.ini配置)

在 pytest.ini 文件中配置 playwright 的选项可以更好地控制测试执行的过程。 在终端输入pytest --help&#xff0c;可以找到playwright的配置参数 目录 1. --browser{chromium,firefox,webkit} 2. --headed 3. --browser-channelBROWSER_CHANNEL 4. --slowmoSLOWMO 5. …

Photos框架 - 自定义媒体选择器(UI列表)

​​​​​​​Photos框架 - 自定义媒体资源选择器&#xff08;数据部分&#xff09; Photos框架 - 自定义媒体选择器&#xff08;UI列表&#xff09;​​​​​​​ Photos框架 - 自定义媒体选择器&#xff08;UI预览&#xff09; Photos框架 - 自定义媒体选择器&#xff0…

规划决策算法(四)---Frenet坐标系

知乎&#xff1a;坐标系转换 1.Frenet 坐标系 什么是 Frenet 坐标系&#xff1a; 为什么使用 Frenet 坐标系&#xff1a; 通常情况&#xff0c;我们只会关注车辆当前距离左右车道线的距离&#xff0c;来判断是否偏离车道&#xff0c;是否需要打方向盘进行方向微调。而不是基于…

【YashanDB知识库】yasdb jdbc驱动集成BeetISQL中间件,业务(java)报autoAssignKey failure异常

问题现象 BeetISQL中间件版本&#xff1a;2.13.8.RELEASE 客户在调用BeetISQL提供的api向yashandb的表中执行batch insert并将返回sequence设置到传入的java bean时&#xff0c;报如下异常&#xff1a; 问题的风险及影响 影响业务流程正常执行&#xff0c;无法获得batch ins…

matlab仿真 数字信号载波传输(下)

&#xff08;内容源自详解MATLAB&#xff0f;SIMULINK 通信系统建模与仿真 刘学勇编著第七 章内容&#xff0c;有兴趣的读者请阅读原书&#xff09; clear all M8; msg[1 4 3 0 7 5 2 6]; ts0.01; T1; %t0:ts:T; t0:ts:T-ts; %x0:ts:length(msg); x0:ts:length(msg)-ts; f…

决策树基础

概述 决策树是一种树型结构&#xff0c;其中每个内部结点表示在一个属性上的测试&#xff0c;每个分支代表一 个测试输出&#xff0c;每个叶结点代表一种类别。决策树学习采用的是自顶向下的递归方法&#xff0c;其基本思想是以信息熵为度量构造一棵熵值下降最快的树&#xff…

一层5x1神经网络绘制训练100轮后权重变化的图像

要完成这个任务&#xff0c;我们可以使用Python中的PyTorch库来建立一个简单的神经网络&#xff0c;网络结构只有一个输入层和一个输出层&#xff0c;输入层有5个节点&#xff0c;输出层有1个节点。训练过程中&#xff0c;我们将记录权重的变化&#xff0c;并在训练100轮后绘制…

github简单地操作

1.调节字体大小 选择options 选择text 选择select 选择你需要的参数就可以了。 2.配置用户名和邮箱 桌面右键&#xff0c;选择git Bash Here git config --global user.name 用户名 git config --global user.email 邮箱名 3.用git实现代码管理的过程 下载别人的项目 git …

反爬虫限制:有哪些方法可以保护网络爬虫不被限制?

目前&#xff0c;爬虫已经成为互联网数据获取最主流的方式。但为了保证爬虫顺利采集数据&#xff0c;需要防范网站的反爬虫机制&#xff0c;降低IP被限制的风险&#xff0c;这样才能提高爬虫工作的效率。那么&#xff0c;如何防止网络爬虫被限制呢&#xff1f;下面介绍几种有效…

dpdk发送udp报文

dpdk接收到udp报文后&#xff0c;自己构造一个udp报文&#xff0c;将收到的报文中的源mac&#xff0c;目的mac&#xff0c;源ip&#xff0c;目的ip&#xff0c;源端口和目的端口交换下顺序填充到新的udp报文中&#xff0c;报文中的负载数据和收到的udp保持一致。 注&#xff1…

Yarn UI 时间问题,相差8小时

位置 $HADOOP_HOME/share/hadoop/yarn/hadoop-yarn-common-2.6.1.jar 查看 jar tf hadoop-yarn-common-2.6.1.jar |grep yarn.dt.plugins.js webapps/static/yarn.dt.plugins.js 解压 jar -xvf hadoop-yarn-common-2.6.1.jar webapps/static/yarn.dt.plugins.js inflated: we…

【文件解析漏洞】实战详解!

漏洞描述&#xff1a; 文件解析漏洞是由于中间件错误的将任意格式的文件解析成网页可执行文件&#xff0c;配合文件上传漏洞进行GetShell的漏洞! IIS解析漏洞&#xff1a; IIS6.X&#xff1a; 方式一:目录解析 在网站下建立文件夹的名字为.asp/.asa 的文件夹&#xff0c;其目…