【C++】unordered_map与unorder_set的封装(哈希桶)

文章目录

  • 前言
  • 一、模板参数的改造
  • 二、模板的特例化操作
  • 三、仿函数的妙用
  • 四、unordered迭代器基本操作
    • 1.const迭代器注意:
    • 2.HashTable与HTIterator的冲突
  • 五、迭代器的构造问题
  • 六、完整代码
    • 1.hash_bucket.h
    • 2.unordered_set.h
    • 3.unordered_map.h

前言

在这里插入图片描述
我们开辟一个指针数组,指针数组中存放我们结点的类型,我们算出元素的下标hashi后,头插在数组的对应位置,数组的位置可以链接一串链表,所以也避免了哈希冲突

一、模板参数的改造

我们这里把pair<K,V>看成一个整体,我们设计模板的时候就不需要考虑是不是键值对类型,需不需要多传一个模板参数的问题,达到了普适性。

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

在map中,T传pair<K,V>类型
在set中,T传K类型

二、模板的特例化操作

我们要想插入一个结点,肯定要知道这个结点插入的位置,所以我们要自己写一个哈希函数的模板来进行下标的计算,但我们int类型之间计算下标很容易,那我们字符串该怎么办?
这个时候就需要模板的特例化了,根据传入的参数不同,具体类型具体分析

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 e : str) {hash *= 131;hash += e;}//字符串的总和再与131相乘,//减少字符串和雷同的情况return hash;}
};

之后再对算出的hash进行取模的操作

三、仿函数的妙用

我们value_type类型用模板参数T代替之后,这个时候就会衍生一个问题,我T可能为键值对类型,我键值对之间怎么比较呢?
例如:T t1与T t2两个变量,我们肯定不能直接比较,肯定要依据他们的键值大小进行比较,所以我们需要自己写一个用于比较的函数,这个时候仿函数刚好能发挥这个用处,可以作为模板参数传入自己写的比较函数

取出他们的键,让他们进行比较,这里set也这样写是为了配合map,因为两者都用的一个哈希桶模板

struct SetKeyOfT {const K& operator()(const K&key) {return key;}};struct MapKeyOfT{const K& operator()(const pair<K, V>& kv){return kv.first;}};

四、unordered迭代器基本操作

1.const迭代器注意:

在这里插入图片描述
这里如果使用const迭代器会报错,因为发生了权限的放大

修改方法:在参数的位置以及定义的时候给HashTable加上const
这样当我们传const类型的时候发生权限的平移,传普通类型的时候发生权限缩小const HashTable<K, T, KeyOfT, HashFunc>* _pht;Node* _node;HTIterator(Node*node, const HashTable<K, T, KeyOfT, HashFunc>* pht):_node(node),_pht(pht){}

2.HashTable与HTIterator的冲突

HashTable中用到了HTIterator,因为要创建出迭代器,而我们HTIterator内部使用到了HashTable中的私有。这就造成了先有鸡还是先有蛋的问题。

解决方法:
在HTIterator的类前面进行前置声明。告诉编译器这个HashTable是存在的
在这里插入图片描述
在HashTable中声明友元在这里插入图片描述

五、迭代器的构造问题

在这里插入图片描述
在unordered_set中我们返回的pair里面的iterator是const类型,但我们哈希桶里面写的Insert中的pair返回的是普通迭代器,因为类模板实例化不同的模板参数就是不同的类型,所以这里const_iterator与iterator我们可以看成是两个不相同的类型,如果我们直接传哈希桶里面的Insert返回值会发生报错,因为类型不匹配。
这个时候我们需要一个函数来将iterator类型转变为const_iterator类型,我们可以从迭代器的拷贝构造下手。

typedef HTIterator<  K,  T,   Ptr,   Ref,   KeyOfT,  HashFunc>  Self;
typedef HTIterator<  K, T, T*, T&, KeyOfT, HashFunc> Iterator;
typedef HashNode<T> Node;
const HashTable<K, T, KeyOfT, HashFunc>* _pht;
Node* _node;HTIterator(const Iterator& it):_node(it._node), _pht(it._pht){}

如果调用这个函数的是普通迭代器iterator,这里就是纯拷贝构造
如果调用这个函数的是const_iterator,那么这个函数就是构造,我们可以传入普通迭代器iterator来构造出const_iterator

六、完整代码

1.hash_bucket.h

#pragma once
#include<vector>
#include<string>namespace hash_bucket {template<class T>
struct HashNode {//结点的定义T _data;HashNode<T>* _next;HashNode(const T& data):_data(data),_next(nullptr){}
};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 hashi = 0;for (auto e : str) {hashi *= 131;hashi += e;}return hashi;}
};template<class K, class T, class KeyOfT, class HashFunc >
class HashTable;
//前置声明
template<class K, class T, class Ptr, class Ref, class KeyOfT, class HashFunc = DefaultHashFunc<K>>
struct HTIterator {typedef HTIterator<  K, T, Ptr, Ref, KeyOfT, HashFunc>  Self;typedef HTIterator<  K, T, T*, T&, KeyOfT, HashFunc> Iterator;typedef HashNode<T> Node;const HashTable<K, T, KeyOfT, HashFunc>* _pht;//引入哈希桶,因为我们进行++操作的时候需要哈希桶数组来确定位置//这里哈希桶要为const类型Node* _node;HTIterator(Node* node, const HashTable<K, T, KeyOfT, HashFunc>* pht):_node(node),_pht(pht){}HTIterator(const Iterator& it):_node(it._node), _pht(it._pht){}Self& operator++() {if (_node->_next) {//当前链表后面还有_node = _node->_next;}else {//当前链表以及走完KeyOfT kot;HashFunc hf;size_t hashi = hf(kot(_node->_data)) % _pht->_table.size();//kot先取出_node->_data中的K值然后hf算出哈希下标++hashi;//从下一个位置开始while (hashi < _pht->_table.size()) {if (_pht->_table[hashi]) {//找不为空的位置_node = _pht->_table[hashi];return*this;}else {hashi++;}}_node = nullptr;}return *this;}Ref operator*() {return _node->_data;}Ptr operator->() {return &_node->_data;}bool operator!=(const Self& s){return _node != s._node;}bool operator==(Self& s) {return _node == s._node;}};template<class K,class T,class KeyOfT,class HashFunc=DefaultHashFunc<K>>
//KeyOfT的作用是取出T里面的K值,因为T有可能为pair类型
class HashTable {typedef HashNode<T> Node;
public:template<class K, class T, class Ptr, class Ref, class KeyOfT, class HashFunc >friend struct HTIterator;//友元的引入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++) {if (_table[i]) {return iterator(_table[i], this);}}return iterator(nullptr, this);}iterator end() {return iterator(nullptr, this);}const_iterator begin()const {for (size_t i = 0; i < _table.size(); i++) {if (_table[i]) {return const_iterator(_table[i], this);}}return const_iterator(nullptr, this);}const_iterator end()const {return const_iterator(nullptr, this);}HashTable(){//构造函数_table.resize(10,nullptr);}~HashTable() {//析构函数for (size_t i = 0; i < _table.size(); i++) {Node* cur = _table[i];while (cur) {Node* first = cur->_next;delete cur;cur = first;}_table[i] = nullptr;}}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 end();}pair<iterator, bool> Insert(const T& data){KeyOfT kot;iterator it = Find(kot(data));if (it != end()){return make_pair(it, false);}HashFunc hf;// 负载因子到1就扩容if (_n == _table.size()){//size_t newSize = _table.size() * 2;size_t newSize = _table.size()*2;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);}bool Erase(const K& key) {HashFunc hf;size_t hashi = hf(key) % _table.size();Node* prev = nullptr;Node* cur = _table[hashi];while (cur) {if (kot(cur->_data) == key) {if (prev == nullptr) {_table[hashi] = nullptr;}else {Node* next = cur->_next;prev->_next = next;}delete cur;return true;}prev = cur;cur = cur->_next;}--_n;return false;}private:vector<Node*> _table;//定义指针数组size_t _n;
};}

2.unordered_set.h

#pragma once
#include"hash_bucket.h"namespace bit {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) {pair< hash_bucket::HashTable<K, K, SetKeyOfT>::iterator,bool>ret= _ht.Insert(key);//先拿到为普通迭代器的iteratorreturn pair<iterator, bool>(ret.first, ret.second);//用普通迭代器构造const迭代器}private:hash_bucket::HashTable<K, K, SetKeyOfT> _ht;};}

3.unordered_map.h

#pragma once
#include"hash_bucket.h"namespace bit {template<class K,class V>class unordered_map {struct MapKeyOfT {const K& operator()(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:hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT> _ht;};}

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

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

相关文章

使用LDA(线性判别公式)进行iris鸢尾花的分类

线性判别分析((Linear Discriminant Analysis &#xff0c;简称 LDA)是一种经典的线性学习方法&#xff0c;在二分类问题上因为最早由 [Fisher,1936] 提出&#xff0c;亦称 ”Fisher 判别分析“。并且LDA也是一种监督学习的降维技术&#xff0c;也就是说它的数据集的每个样本都…

驱动开发--汇总

一&#xff0c;【驱动相关概念】 1&#xff0c;什么是驱动 能够驱使硬件实现特定功能的软件代码 根据驱动程序是否依赖于系统内核将驱动分为裸机驱动和系统驱动 2&#xff0c;逻辑驱动和系统驱动的区别 裸机驱动&#xff1a;编写的驱动代码中没有进行任何内核相关API的调用…

Flutter插件的制作和发布

Flutter制作插件有两种方式&#xff08;以下以android和ios为例&#xff09;&#xff1a; 目录 1.直接在主工程下的android和ios项目内写插件代码&#xff1a;2.创建独立Flutter Plugin项目&#xff0c;制作各端插件后&#xff0c;再引入项目&#xff1a;1. 创建Flutter Plugin…

Webpack打包CSS文件,解决You may need an appropriate loader to handle this file type报错

在项目文件夹下创建webpack.config.js文件&#xff0c;该文件就是Webpack的配置文件 注意&#xff1a;该文件中遵循Node.js的代码格式规范 &#xff0c;需要对导出配置文件中的内容 Webpack在默认情况下只能打包js文件&#xff0c;如果我们希望他能够打包其他类型的文件&#…

物联网安全优秀实践:2023年设备保护指南

物联网的发展可谓是革命性的&#xff0c;数十亿台设备实时互连、通信和共享数据。因此&#xff0c;考虑物联网安全的最佳实践至关重要。 物联网的重要性日益上升 在数字时代&#xff0c;物联网(IoT)已成为一股革命力量&#xff0c;重塑了企业运营和个人生活方式。从调节家庭温…

堆的OJ题

&#x1f525;&#x1f525; 欢迎来到小林的博客&#xff01;&#xff01;       &#x1f6f0;️博客主页&#xff1a;✈️林 子       &#x1f6f0;️博客专栏&#xff1a;✈️ 小林的算法笔记       &#x1f6f0;️社区 :✈️ 进步学堂       &am…

中国数据库走向国际的门槛: 15分钟准则

在十五分钟之内跑通第一条SQL 笔者曾经负责国内某Top云厂商的数据库的海外业务和产品设计。简单的说包括TP&#xff0c;AP&#xff0c;NoSQL和Utility的所有数据库相关产品&#xff0c;负责除中国大陆以外所有的业务和客户。三年时间撞墙的教训实在不少。 在官方宣传上&#…

Scala 高阶:Scala中的模式匹配

一、概述 Scala中的模式匹配&#xff08;case&#xff09;类似于Java中的switch...case&#xff0c;但是Scala的模式匹配功能更为强大。通过模式匹配&#xff0c;可以匹配更复杂的条件和数据结构&#xff0c;包括常量、类型、集合、元组等。而 Java 的 switch 语句只能用于匹配…

算法分析与设计编程题 贪心算法

活动安排问题 题目描述 解题代码 vector<bool> greedySelector(vector<vector<int>>& intervals) {int n intervals.size();// 将活动区间按结束时间的从小到大排序auto cmp [](vector<int>& interval1, vector<int>& interval2…

YOLOv5,YOLOv8添加ASFF(自适应空间特征融合)

ASFF&#xff1a;Adaptively Spatial Feature Fusion (自适应空间特征融合) 论文来源&#xff1a;Learning Spatial Fusion for Single-Shot Object Detection 代码地址&#xff1a;ASFF 1.背景 不同特征尺度之间的不一致性是基于特征金字塔的单阶段检测器的主要缺陷。 本文…

【VastbaseG100】 FATAL: The account has been locked.

使用VastbaseG100 数据库&#xff0c;查询数据报错。 org.postgresql.util.PSQLException: FATAL: The account has been locked. 帐户已被锁定。 解锁账户呗 ALTER ROLE doc ACCOUNT UNLOCK;ALTER ROLE 用户名 ACCOUNT UNLOCK; 修改密码 ALTER ROLE doc IDENTIFIED BY ZhangS…

Git小乌龟不弹add push commit的方法

1.关于使用Git小乌龟无法弹出Add菜单的问题 第一次使用小乌龟软件&#xff0c;发现可以正常将程序从Gitee仓库中克隆到本地&#xff0c;但是在将本地的程序上传到Gitee仓库中时&#xff0c;TortoiseGit无法弹出Add那一系列菜单&#xff0c;如下图所示&#xff1a; 2.解决方法 …

开源日报 0821:帮你修复老旧照片

这篇文章总结了几个开源项目的特点和优势。其中包括了 Python 资源列表、金融研究工具、动画精灵程序、游戏和旧照片修复项目等。这些项目提供了丰富的功能和技术支持&#xff0c;用户可以根据自己的需求进行定制和改进。总的来说&#xff0c;这些开源项目为开发者和用户提供了…

安防视频/视频汇聚平台EasyCVR使用onvif探测添加设备通道详细步骤来啦!

视频云存储/安防监控EasyCVR视频汇聚平台基于云边端智能协同&#xff0c;支持海量视频的轻量化接入与汇聚、转码与处理、全网智能分发、视频集中存储等。音视频流媒体视频平台EasyCVR拓展性强&#xff0c;视频能力丰富&#xff0c;具体可实现视频监控直播、视频轮播、视频录像、…

大数据学习1.1-Centos8虚拟机安装

1.创建新的虚拟机 2.选择稍后安装OS 3.选择Linux的CentOS8 4.选择安装路径 5.分配20g存储空间 6.自定义硬件 7.分配2g内存 8.分配2核处理器 9.选择镜像位置 10.开启虚拟机安装 推荐密码设置为root

WebGL 用鼠标控制物体旋转

目录 鼠标控制物体旋转 如何实现物体旋转 示例程序&#xff08;RotateObject.js&#xff09; 代码详解 示例效果 鼠标控制物体旋转 有时候&#xff0c;WebGL程序需要让用户通过鼠标操作三维物体。这一节来分析示例程序RotateObject&#xff0c;该程序允许用户通过拖动&…

DC/DC开关电源学习笔记(十)Buck降压电路仿真及工程应用实例

(十)Buck降压电路仿真及工程应用实例 1. 仿真应用实例1.1 案例一1.2 案例二2. 工程应用实例2.1 数字DC/DC应用实例2.2 模拟DC/DC应用实例1. 仿真应用实例 1.1 案例一 仿真技术要求输入:输入电压30~90V,输出电压28V,输出电流最大10A,开关频率100KHz。我们按照参数极限工…

一、八大排序(sort)

文章目录 一、时间复杂度&#xff08;一&#xff09;定义&#xff1a;常数操作 二、空间复杂度&#xff08;一&#xff09;定义&#xff1a; 三、排序&#xff08;一&#xff09;选择排序1.定义2.代码3.特性 &#xff08;二&#xff09;冒泡排序1.定义2.代码3.特性 &#xff08…

雷龙CS SD NAND(贴片式TF卡)性能体验及应用

前段时间有幸得到了雷龙出品的贴片式的TF卡的芯片及转接板&#xff0c;从而对其产品进行了相应的了解和测评。 从获得的相关资料看&#xff0c;雷龙出品的贴片式芯片分为两类&#xff0c;即BOW型和AOW型&#xff0c;其中BOW型为第一代产品&#xff0c;属商业级&#xff1b;AOW…

【网络协议】Http-中

搜索引擎&#xff1a;搜索引擎是指根据一定的策略、运用特定的计算机程序从互联网上采集信息&#xff0c;在对信息进行组织和处理后&#xff0c;为用户提供检索服务&#xff0c;将检索的相关信息展示给用户的系统。搜索引擎是工作于互联网上的一门检索技术&#xff0c;它旨在提…