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

news2024/11/15 7:05:05

目录

​编辑

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

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();//计算hashi

			Node* 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();//计算hashi
			Node* 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.coloradmin.cn/o/1469170.html

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

相关文章

吴恩达deeplearning.ai:矩阵运算代码实战

神经网络向量化指的是将输入数据转化为向量形式&#xff0c;以便于神经网络的处理。向量化的作用包括以下几点&#xff1a; 提高计算效率&#xff1a;使用向量化的输入数据可以进行并行计算&#xff0c;加速神经网络的训练和推断过程。 减少存储空间&#xff1a;向量化可以将…

一种确定FET小信号等效电路的新方法

来源&#xff1a;A New Method for Determining the FET Small-Signal Equivalent Circuit&#xff08;88年 TMTT&#xff09; 摘要 - 提出了一种确定FET&#xff08;场效应晶体管&#xff09;小信号等效电路的新方法。该方法包括在低频段直接测定器件的外在和内在小信号参数。…

STM32_DS18B20_1_芯片简介及初始化配置

DS18B20介绍 DS18B20数字温度计提供9位到12位摄氏度的温度测量&#xff0c;并具有非易失性&#xff0c;用户可编程的上下触发点的报警功能。DS18B20通过1线总线进行通信&#xff0c;根据定义&#xff0c;该总线只需要一条数据线&#xff0c;即可与中央微处理器进行通信…

给定一个边与边可能相交的多边形,求它的轮廓线

大家好&#xff0c;我是前端西瓜哥。 最近遇到一个需求&#xff0c;给定一个多边形&#xff08;边与边可能相交&#xff09;&#xff0c;求这个多边形的轮廓线。 需要注意的是&#xff0c;轮廓线多边形内不能有空洞&#xff0c;使用的不是常见的非零绕数规则&#xff08;nonze…

2.23 Qt day4 事件机制+定时器事件+键盘事件+鼠标事件

思维导图&#xff1a; 做一个闹钟&#xff0c;在行编辑器里输入定闹钟的时间&#xff0c;时间到了就语音播报文本里的内容&#xff0c;播报五次 widget.h&#xff1a; #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include<QDebug>//输出类 #include<…

JSON(javaScript Object Notation,Js对象标记)—我耀学IT

Json是一种轻量级的数据交换格式&#xff0c;目前使用非常广泛&#xff0c;是一种轻量级的数据交换格式。易于人阅读和编写&#xff0c;可以在多种语言之间进行数据交换 。同时也易于机器解析和生成 1.1json的值: 值可以是对象、数组、数字、字符串或者三个字面值(false、nul…

990-05产品经理:为什么商业价值是 IT 成功的关键

In today’s digital era, CIOs must shift(转移) their priorities from cost cutting to driving revenue(收入), and from process engineering to exploiting data if they want to achieve a set of broader business outcomes. Furthermore, understanding how to measur…

Mac OS 下载安装与破解Typora

文章目录 下载Typora破解Typora1. 进入安装目录2. 找到并打开Lincense文件3. 修改激活状态4. 重新打开Typora 下载Typora 官网地址&#xff1a;typora官网 下载最新Mac版&#xff0c;正常安装即可 破解Typora 打开typora,可以看到由于未激活&#xff0c;提示使用期限还剩下15…

09 呼吸灯

呼吸灯简介 呼吸灯实际展示的效果就是一个 LED 灯的亮度由亮到暗&#xff0c;再由暗到亮的变化过程&#xff0c;并且该过程是循环往复的&#xff0c;像呼吸一样那么有节奏。 呼吸灯通常是采用 PWM(Pulse Width Modulation&#xff0c;即脉冲宽度调制) 的方式实现&#xff0c;在…

超强随机短视频源码自带视频带支付源码

1.开启是否连续自动播放 2.支持手动点击看下一个 3.支持引流跳官方地址&#xff0c;产品地址&#xff0c;可以设置跳转地址 4.简洁大气&#xff0c;支持网站基础信息设置 5.支持设置定时多少时间弹广告 6.支持PC手机设置弹广告图片与点击后跳转链接 源码免费下载地址专业…

【Unity】双击C#脚本文件以单个文件打开(Visual Studio)、父类找不到、引用找不到、无法跳转等问题

问题&#xff1a;新安装一个Unity后&#xff0c;突然发现在工程里双击C#脚本&#xff0c;会一个一个打开&#xff0c;虽然也是用VS软件打开了&#xff0c;但是它无法被正确识别为Unity工程的C#脚本&#xff0c;也就是说所有命名空间无效&#xff0c;因为没关联上整个工程的解决…

OSCP靶场--Slort

OSCP靶场–Slort 考点(1.php 远程文件包含 2.定时任务提权) 1.nmap扫描 ┌──(root㉿kali)-[~/Desktop] └─# nmap 192.168.178.53 -sV -sC -p- --min-rate 5000 Starting Nmap 7.92 ( https://nmap.org ) at 2024-02-24 04:37 EST Nmap scan report for 192.168.178.53 …

Windows安装PHP及在VScode中配置插件,使用PHP输出HelloWorld

安装PHP PHP官网下载地址(8.3版本)&#xff1a;PHP For Windows&#xff1a;二进制文件和源代码发布 点击下载.zip格式压缩包&#xff1a; 历史版本在Old archives中下载。推荐在Documentation download中下载官方文档&#xff0c;方便学习。 下载完成后在一个顺眼的地方解压压…

Spring Boot 项目集成camunda流程引擎

使用camunda开源工作流引擎有&#xff1a;通过docker运行、使用springboot集成、部署camunda发行包、基于源代码编译运行等多种方式。 其中&#xff0c;通过源代码编译运行的方式最为复杂&#xff0c;具体参考&#xff1a;https://lowcode.blog.csdn.net/article/details/1362…

vivado VHDL Objects、VHDL实体描述

VHDL对象包括&#xff1a;信号、变量、常量和运算符。 信号 在中声明VHDL信号&#xff1a; •体系结构声明部分&#xff1a;在该体系结构内的任何位置使用VHDL信号。 •一个块&#xff1a;在该块中使用VHDL信号。 使用<信号分配运算符分配VHDL信号。 signal sig1 : std…

企业计算机服务器中了malloxx勒索病毒怎么办?Malloxx勒索病毒解密数据恢复

网络技术的不断更新与发展&#xff0c;为企业的发展提供了强有力数据支撑&#xff0c;在企业的生产运营过程中&#xff0c;企业数据扮演着重要的角色&#xff0c;通过企业数据可以更好地总结调整企业的规划发展方向与日常数据统计&#xff0c;但利用网络技术的支撑就要防范网络…

软考41-上午题-【数据库】-关系代数运算3-外连接

一、外连接 连接的拓展&#xff0c;处理由于连接运算而缺失的信息。 1-1、回顾自然连接 1-2、左外连接 示例&#xff1a; 左边的表&#xff0c;数值是全的 1-3、右外连接 示例&#xff1a; 右边的表&#xff0c;数值是全的 1-4、全外连接 示例&#xff1a; 自然连接左外连接…

Sora:颠覆性AI视频生成工具

Sora是一款基于人工智能&#xff08;AI&#xff09;技术的视频生成工具&#xff0c;它彻底改变了传统视频制作的模式&#xff0c;为创作者提供了高效、便捷、高质量的视频内容生成方式。通过深度学习和自然语言处理等先进技术&#xff0c;Sora实现了从文字描述到视频画面的自动…

备考2024年汉字小达人:历年考题练一练-18道选择题

今天为大家分享汉字小达人的备考学习资源&#xff0c;通过参加没有报名费、人人可参加的汉字小达人比赛&#xff0c;激发孩子学习语文的兴趣&#xff0c;并且提升语文学习成绩。 汉字小达人的两轮比赛&#xff08;区级自由报名活动、市级活动&#xff09;的选择题主要有六种题型…

Mean Teacher的调研与学习

Mean Teacher的调研与学习 0 FQA:1 Mean Teacher1.1 Mean Teacher简介1.2 回顾Π-Model 和 Temporal Ensembling1.3 Mean Teacher 0 FQA: Q1&#xff0c;什么是Mean Teacher&#xff1f; MT的训练方式是怎样的&#xff1f;A1: Mean Teacher是一种基于一致性正则化的半监督学习…