C++基础学习——哈希表的封装

news/发布时间2024/5/15 18:35:22

目录

​编辑

一,实现一个可封装的哈希表

1,哈希表的节点

 2,哈希表的成员

 3,哈希表成员方法的实现

 4,迭代器的实现

 5,在哈希表中加入迭代器

二,封装哈希表

1,unorder_map封装

2,unordered_set的封装

 


一,实现一个可封装的哈希表

1,哈希表的节点

在哈希表的封装中,分为两类:unordered_map,unordered_set。其中map的元素类型为pair<K,V>类型,set的类型为K类型。

Node实现如下:

   template<class T>struct Node{Node<T>* _next;T val;};

 2,哈希表的成员

哈希表的成员有两个,一个是记录哈希表节点个数的成员_n,一个是成员为Node*类型的vector<Node*>数组。

HashTable实现如下:

template<class T>class HashTables{typedef Node<T> Node;public:private:vector<Node*>_tables;size_t _n;};

 3,哈希表成员方法的实现

(1),Find(V&key)方法

Find方法实现的是在一个哈希表中寻找某个值存不存在。步骤如下:1,找到这个值对应的hashi(在哈希表中的下标)。2,遍历这个下标上挂载的链表上是否有该值。3,有则返回true,没有就返回false.

要找到hashi: 先实现两个方法Getkey()与GetHashi()

Getkey():

    template <class K,class V>struct Getkey{K operator()(const V& val)//一般的成员直接返回val,遇到特殊pair类型的则返回val.first(先不实现){return val;}};

 GetHashi():

template <class T>//一般情况下struct GetHashi{size_t operator(T& val){return (size_t)val;}};template<>//模板特化,当这个成员的类型为string类型时struct GetHashi<string>{size_t operator()(string& val){size_t ret = 0;for (int i = 0;i < val.size();i++){ret += ret * 31 + val[i];}return ret;}};

Find():

       bool Find(V& val){size_t hashi = GetHashi(Getkey(val)) % _tables.size();//计算hashiNode* cur = _tables[hashi];//找到对应的位置上的链表while (cur)//开始寻找{if (cur->val == _val) return true;}return false;}

(2),Insert(T&key)

 实现插入函数:(1),满载时要扩容,扩容逻辑是创建一个新的vector<Node*>temp并且把这个表的大小扩为旧表的二倍。 (2),将旧表中的元素拆下来头插到新表中。(3),将就表中的值全部变为nullptr,防止二次析构。(4),将新表交换给旧表

Insert函数代码: 

bool Insert(const V& val){if (Find(val)) return false;//存在过则直接返回插入失败if (_n == _tables.size())//检查是否需要扩容{int newsize = 2*(_n == 0? 1 : _n);//新的大小vector<Node*>temp;//开一个临时数组temp.resize(newsize);//扩大为原来哈希表的二倍for (int i = 0;i < _tables.size();i++){Node* cur = _tables[i];Node* next = nullptr;if (cur){while (cur){size_t hashi = GetHashi(Getkey(cur->_val))%_tables.size();//计算新的哈希值//保存下一个值,并更新就哈希表上的值next = cur->_next;_tables[i] = next;//头插到新的哈希表中cur->_next = temp[hashi];temp[hashi] = cur;cur = next;}}}//扩容好后便夺回来for (int i = 0;i < _tables.size();i++){_tables[i] = nullptr;}_tables.swap(temp);}int hashi = GetHashi(Getkey(val))%_tables.size();Node* newNode = new Node(val);//头插newNode->_next = _tables[hashi];_tables[hashi] = newNode;_n++;return true;}

 (3)Erase(V&val)

实现:先找到这个值,再删除。

Erase(V&val)代码:

       bool Erase(const V& val){int hashi = GetHashi(Getkey(val)) % _tables.size();//计算hashiNode* pre = nullptr;//记录hashi前面的指针Node* cur = _tables[hashi];//当前指针while (cur){if (cur->_val == val)//有相同的便开始执行删除逻辑{if(pre)pre->_next = cur->_next;//加上条件防止头删出错delete cur;cur = nullptr;_n--;return true;}pre = cur;cur = cur->_next;}return false;}

 4,迭代器的实现

(1),迭代器的成员

迭代器的成员:1,一个Node*类型的成员_node(因为迭代器就是一种模拟指针的行为所以要有哈希表元素的指针)。2,哈希表的指针和当前的_node对应的位置(主要是为了方便实现++)。

代码如下:

        typedef HTiterator<K, V, ref, ptr, Getkey, GetHashi> self;//迭代器类型typedef Node<V> Node;Node* _node;//哈希表成员的指针typedef HashTables<K, V, Getkey, GetHashi>*  pht;pht _pht;//哈希表的指针size_t _hashi;//迭代器在哈希表中的位置

(2)迭代器++的实现

1,找到下一个不为nullptr的点(这里要用到哈希表的指针和当前指针的位置) 。  2,返回一个迭代器。3,在实现++之前得实现一个迭代器的构造函数。

构造函数代码:

        HTiterator(Node* node,pht Hp,size_t hashi)//构造函数:_node(node),_pht(Hp),_hashi(hashi){}

实现迭代器operator++()代码:

//实现++self operator ++(){if (_node->_next){_node = _node->_next;}else{_hashi++;while (_hashi < _pht->_tables.size()){if (_pht->_tables[_hashi]){_node = _pht->_tables[_hashi];return HTiterator(_node, _pht, _hashi);//找到了直接返回构造的iterator}_hashi++;}_node = nullptr;//找不到就将_node置为nullptr在构造}return  HTiterator(_node, _pht, _hashi);}

(3)实现operator*(),operator->(),operator!=()

*:返回值是_node里面的val。->:返回值是_node->val的地址。!=:比较的是_node

 代码:

        ref operator*(){return _node->_val;}ptr operator->(){return& _node->_val;}bool operator!=(const self& s){return _node != s._node;}

 5,在哈希表中加入迭代器

       typedef HTiterator<K, V, V&, V*> iterator;//定义一个迭代器类型iterator begin()//实现begin(){for (int hashi = 0;hashi < _tables.size();hashi++){if (_tables[hashi]) return iterator(_tables[hashi], this, hashi);}return end();}iterator end()//实现end(){return iterator(nullptr, this, -1);}

二,封装哈希表

1,unorder_map封装

(1)unordered_map的成员

unordered_map的底层便是一个哈希表,所以unordered_map的成员便是一个哈希表。

代码:

cq::HashTables<K,pair<K, V>, Getkey<K,V>> HT;

(2)unordered_map的方法

在实现这些方法之前,先得把哈希表里的Insert()和Find()方法的返回值改一下,改成如下形式:pair<iterator,bool>,方便后面的operator[]的实现。

代码:

pair<iterator,bool> Insert(const V& val)//改成pair<iterator,bool>类型{iterator it = Find(val);if (it != end()) return make_pair(it, false);if (_n == _tables.size())//检查是否需要扩容{int newsize = 2*(_n == 0? 1 : _n);//新的大小vector<Node*>temp;//开一个临时数组temp.resize(newsize);for (int i = 0;i < _tables.size();i++){Node* cur = _tables[i];Node* next = nullptr;if (cur){while (cur){size_t hashi = GetHashi(Getkey(cur->_val))%_tables.size();//计算新的哈希值//保存下一个值,并更新就哈希表上的值next = cur->_next;_tables[i] = next;//头插到新的哈希表中cur->_next = temp[hashi];temp[hashi] = cur;cur = next;}}}//扩容好后便夺回来for (int i = 0;i < _tables.size();i++){_tables[i] = nullptr;}_tables.swap(temp);}size_t hashi = GetHashi(Getkey(val))%_tables.size();Node* newNode = new Node(val);//头插newNode->_next = _tables[hashi];_tables[hashi] = newNode;_n++;return   make_pair(iterator(newNode, this, hashi),true);}

(2) 实现V& operator[](K&key)

operator[]的作用是让我们能够通过key值来访问val值。

代码:

       V& operator[](const K& key){pair<iterator,bool> ret = HT.Insert(make_pair(key,V()));return  ret.first->second;//返回的是一个pair<iterator,bool>,first代表iterator,然后再调用iterator的->找到val值}

(3)unordered_map里其它的成员方法

其它的成员方法都是通过调用哈希表里面实现的方法实现的。

       iterator begin(){return HT.begin();}iterator end(){return HT.end();}pair<iterator, bool> insert(const pair<K,V>& val){return HT.Insert(val);}bool erase(const V& val){return HT.Erase(val);}pair<iterator,bool> Find(const V& val){return HT.Find(val);}

unordered_map封装代码:

	template < class K,class V>struct Getkey{K operator()(const pair<K,V>& val){return val.first;}};template <class K, class V>class my_unordered_map{typedef typename cq::HashTables<K, pair<K,V>, Getkey<K,V>>::iterator iterator;public:iterator begin(){return HT.begin();}iterator end(){return HT.end();}pair<iterator, bool> insert(const pair<K,V>& val){return HT.Insert(val);}bool erase(const V& val){return HT.Erase(val);}pair<iterator,bool> Find(const V& val){return HT.Find(val);}V& operator[](const K& key){pair<iterator,bool> ret = HT.Insert(make_pair(key,V()));return  ret.first->second;}private:cq::HashTables<K,pair<K, V>, Getkey<K,V>> HT;};

 

2,unordered_set的封装

unordered_set的封装与unordered_map的封装类似。但是不用实现operator[]。

 代码如下:

	template < class K>//set的模板参数只要一个struct Getkey{K operator()(const K& val){return val;}};template <class K>class my_unordered_set{typedef typename cq::HashTables<K, K, Getkey<K>>::iterator iterator;public:iterator begin(){return HT.begin();}iterator end(){return HT.end();}pair<iterator, bool> insert(const K& val){return HT.Insert(val);}bool erase(const K& val){return HT.Erase(val);}pair<iterator, bool> Find(const K& val){return HT.Find(val);}private:cq::HashTables<K, K, Getkey<K>> HT;//内部成员哈希表};

 

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

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

相关文章

HarmonyOS—代码Code Linter检查

Code Linter代码检查 Code-Linter针对ArkTS/TS代码进行最佳实践、编程规范方面的检查&#xff0c;目前还会检查ArkTS语法规则。开发者可根据扫描结果中告警提示手工修复代码缺陷&#xff0c;或者执行一键式自动修复&#xff0c;在代码开发阶段&#xff0c;确保代码质量。 检查…

unity学习(41)——创建(create)角色脚本(panel)——UserHandler(收)+CreateClick(发)——发包!

1.客户端的程序结构被我精简过&#xff0c;现在去MessageManager.cs中增加一个UserHandler函数&#xff0c;根据收到的包做对应的GameInfo赋值。 2.在Model文件夹下新增一个协议文件UserProtocol&#xff0c;内容很简单。 using System;public class UserProtocol {public co…

基于django的购物商城系统

摘要 本文介绍了基于Django框架开发的购物商城系统。随着电子商务的兴起&#xff0c;购物商城系统成为了许多企业和个人创业者的首选。Django作为一个高效、稳定且易于扩展的Python web框架&#xff0c;为开发者提供了便捷的开发环境和丰富的功能模块&#xff0c;使得开发购物商…

基于Java SSM框架实现高校网课管理系统项目【项目源码+论文说明】

基于java的SSM框架实现高校网课管理系统演示 摘要 随着移动应用技术的发展&#xff0c;越来越多的学生借助于移动手机、电脑完成生活中的事务&#xff0c;许多的行业也更加重视与互联网的结合&#xff0c;以提高教学的教育水平和寻求更高的经济利益。针对高校网课管理系统&…

EI论文联合复现:含分布式发电的微网/综合能源系统储能容量多时间尺度线性配置方法程序代码!

适用平台&#xff1a;Matlab/Gurobi 程序提出了基于线性规划方法的多时间尺度储能容量配置方法&#xff0c;以满足微电网的接入要求为前提&#xff0c;以最小储能配置容量为目标&#xff0c;对混合储能装置进行容量配置。程序较为基础&#xff0c;算例丰富、注释清晰、干货满满…

[linux]进程间通信(IPC)———共享内存(shm)(什么是共享内存,共享内存的原理图,共享内存的接口,使用演示)

一、什么是共享内存 共享内存区是最快的&#xff08;进程间通信&#xff09;IPC形式。一旦这样的内存映射到共享它的进程的地址空间&#xff0c;这些进程间数据传递不再涉及到内核&#xff0c;换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据。注意&#xff1a;…

亿道丨三防平板丨加固平板丨三防加固平板丨改善资产管理

库存资产管理中最重要的部分之一是准确性&#xff1b;过时的库存管理技术会增加运输过程中人为错误、物品丢失或纸张损坏的风险。如今随着三防平板电脑的广泛使用&#xff0c;库存管理也迎来了好帮手&#xff0c;通过使用三防平板电脑能够确保库存管理、数据存储和记录保存的准…

Linux-用户和权限(黑马学习笔记)

认识root用户 root用户&#xff08;超级管理员&#xff09; 无论是Windows、MacOS、Linux均采用多用户的管理模式进行权限管理。 ● 在Linux系统中&#xff0c;拥有最大权限的账户名为&#xff1a;root&#xff08;超级管理员&#xff09; ● 而在前期&#xff0c;我们一直…

apidoc接口文档的自动更新与发布

文章目录 一、概述二、环境准备三、接口文档生成1. 下载源码2. 初始化3.执行 四、文档发布五&#xff0c;配置定时运行六&#xff0c;docker运行 一、概述 最近忙于某开源项目的接口文档整理&#xff0c;采用了apidoc来整理生成接口文档。 apidoc是一个可以将源代码中的注释直…

AWS安全组是什么?有什么用?

最近看到小伙伴在问&#xff0c;AWS安全组是什么&#xff1f;有什么用&#xff1f;今天我们大家就来简单聊聊&#xff0c;仅供参考哦&#xff01; AWS安全组是什么&#xff1f;有什么用&#xff1f; 【回答】&#xff1a;AWS安全组是一种虚拟防火墙&#xff0c;用于控制进出…

Zookeeper特性与节点数据类型详解

Zookeeper特性与节点数据类型详解 Zookeeper简介 一个基于观察者模式&#xff0c;主要是用来解决分布式集群应用系统一致性问题的协调框架&#xff0c;基于CP机制 本质是一个分布式的小文件存储系统(文件系统监听机制)提供基于类似于文件系统的目录树方式的数据存储&#xff…

Git合并固定分支的某一部分至当前分支

在 Git 中&#xff0c;通常使用 git merge 命令来将一个分支的更改合并到另一个分支。如果你只想合并某个分支的一部分代码&#xff0c;可以使用以下两种方法&#xff1a; 1.批量文件合并 1.1.创建并切换到一个新的临时分支 首先&#xff0c;从要合并的源分支&#xff08;即要…

Maven的下载安装配置教程

一、简单了解一下什么是Maven Maven就是一款帮助程序员构建项目的工具&#xff0c;我们只需要告诉Maven需要哪些Jar 包&#xff0c;它会帮助我们下载所有的Jar&#xff0c;极大提升开发效率。 1.Maven翻译为“专家“&#xff0c; ”内行”的意思&#xff0c;是著名Apache公司下…

ElasticSearch索引数据备份与恢复

索引数据备份 在磁盘创建备份目录并授权 # 创建备份目录 /home/esbackup # 授权 chmod 777 /home/esbackup修改配置文件elasticsearch.yml echo path.repo: ["/home/esbackup"] >> /etc/elasticsearch/elasticsearch.yml重启elasticsearch(我是docker创建的…

矿产达人小程序修复前端

应用介绍 本文来自&#xff1a;矿产达人小程序修复前端 - 源码1688 矿产达人小程序&#xff1a; 矿产小游戏小程序是一款以矿产资源为主题的休闲娱乐游戏。以下是该小程序的主要功能特点&#xff1a; 游戏画面精美&#xff1a;小程序采用卡通化的设计风格&#xff0c;画面色…

【操作系统】磁盘存储空间的管理

实验5 磁盘存储空间的管理 一、实验目的 磁盘是用户存放程序和数据的存储设备&#xff0c;磁盘管理的主要目的是充分有效地利用磁盘空间。本实验模拟实现磁盘空间的分配与回收&#xff0c;使学生对磁盘空间的管理有一个较深入的理解。 二、实验内容 实验任务&#xff1a;用位…

记录 | docker内执行apt update报错GPG error

1. 执行 sudo apt-get update 命令时遇到这个错误&#xff0c;是服务器没有这个公钥的意思 rootadmin:~# sudo apt-get update Get:1 https://download.docker.com/linux/ubuntu focal InRelease [36.2 kB] Err:1 https://download.docker.com/linux/ubuntu focal InRelease T…

Redis 分布式锁

什么是分布式锁 在一个分布式的系统中&#xff0c;也会涉及到多个节点访问同一个公共资源的情况。此时就需要通过锁来做互斥控制&#xff0c;避免出现类似于“线程安全”的问题。 而 java 的 synchronized 或者 C 的 std::mutex&#xff0c;这样的锁都是只能在当前进程中生效…

基于yolov5的工地安全帽检测,可进行图像目标检测,也可进行视屏和摄像检测(pytorch框架)【python源码+UI界面+功能源码详解】

功能演示&#xff1a; 基于yolov5的工地安全帽检测系统&#xff0c;支持图像检测&#xff0c;视频检测和实时摄像检测功能_哔哩哔哩_bilibili &#xff08;一&#xff09;简介 基于yolov5的工地安全帽检测系统是在pytorch框架下实现的&#xff0c;这是一个完整的项目&#x…

oppo手机如何录屏?解锁录屏新功能!

“最近换了一款oppo手机&#xff0c;感觉它的拍照功能真的很强大。但除此之外&#xff0c;我发现oppo还有许多隐藏功能&#xff0c;比如录屏。但我尝试了很久&#xff0c;都没找到录屏的开关在哪里。有没有哪位oppo用户知道怎么打开这个功能呢&#xff1f;” 随着科技的不断发…
推荐文章