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

目录

​编辑

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

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;//内部成员哈希表


	};

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【hot100】跟着小王一起刷leetcode -- 49. 字母异位词分组

【【hot100】跟着小王一起刷leetcode -- 49. 字母异位词分组 49. 字母异位词分组题目解读解题思路代码实现 总结 49. 字母异位词分组 题目解读 49. 字母异位词分组 ok&#xff0c;兄弟们&#xff0c;咱们来看看这道题&#xff0c;很明显哈&#xff0c;这里的关键词是字母异位…

2024生物发酵展全面进行-飞翔泵业制造

参展企业介绍 江苏飞翔泵业制造有限公司始建于上世纪八十年代&#xff0c;二00一年根据《公司法》组建江苏飞翔泵业制造有限公司。公司集科研、设计、生产、经营、服务为一体&#xff0c;企业性质为有限责任公司。现为中国石化集团公司物资资源一级网络成员厂&#xff0c;中国石…

备考2025年考研数学(一)真题练习和解析——填空题

今天距离2025年考研预计还有10个月的时间&#xff0c;看起来挺长&#xff0c;但是对于备考2025年考研的同学来说&#xff0c;必须用好每一天。为了帮助大家提升考研数学一的成绩&#xff0c;我收集整理了1987-2024年的考研数学一的真题和解析&#xff0c;并把2015-2024年十年的…

抖店是怎么运营做起来的?抖音小店每天都需要做什么?新手必看!

大家好&#xff0c;我是电商花花。 很多人疑惑开抖音小店之后&#xff0c;选好商品上架之后每天都需要做什么&#xff1f; 不少新手在开了抖音小店之后每天除了选品之后就不知道要做些什么了。 今天给大家分享一下我们每天做抖音小店的工作内容有哪些&#xff0c;如果你是新…

matlab生成模拟的通信信号

matlab中rand函数生成均匀随机分布的随机数&#xff0c;randn生成正态分布的随机数&#xff1b; matlab来模拟一个通信信号&#xff1b; 通信信号通过信道时&#xff0c;研究时认为它会被叠加上服从正态分布的噪声&#xff1b; 先生成随机信号模拟要传输的信号&#xff0c;s…

【一】【SQL】表的增删查改(部分)

表之“增”操作 建表的操作 mysql> create table students(-> id int unsigned primary key auto_increment,-> sn int unsigned unique key,-> name varchar(20) not null,-> qq varchar(32) unique key-> ); Query OK, 0 rows affected (0.03 sec)mysql&g…

试一下newb,还是有错误呀

解题&#xff1a;原式&#xff1d; 2. 在递增的等比数列 ( a n ) (a_n) (an​)中&#xff0c;若 ( a 3 − a 1 5 2 ) (a_3 - a_1 \frac{5}{2}) (a3​−a1​25​), ( a 2 3 ) (a_2 3) (a2​3), 则公比 (q) A. ( 4 3 ) ( \frac{4}{3} ) (34​) B. ( 3 2 ) ( \frac{3}{2} …

创建vue3项目(基础)

首先打开自己的目录文件输入指令cmd 出现命令行工具 输入指令vue create 项目名称 按回车 选择第三个自己配置 根据需求选择 回车 选择自己需要的版本 出现这个 一直按回车大约5下或者6下 创建完毕 结束 感谢观看

Flutter(二):Row、Column 布局

MaterialApp 对于 MaterialApp&#xff0c;组件提供了一些默认的属性&#xff0c;如AppBar、标题、背景颜色等&#xff0c;你可以默认使用它们 import package:flutter/material.dart;void main() {runApp(const App()); }class App extends StatelessWidget {const App({super…

《名作欣赏》期刊投稿方向投稿邮箱

《名作欣赏》杂志是由国家新闻出版总署批准的正规学术期刊。文学是用语言塑造形象反映社会生活的一种语言艺术&#xff0c;是自觉、独立而又面向整个社会的艺术&#xff0c;是文化中极具强烈感染力的重要组成部分&#xff0c;亦是对美的体现。在现代社会&#xff0c;物欲横流&a…

用matlab实现交通分布预测方法——增长系数法

前言 对于我的第一篇文章&#xff1a;Matlab实现交通分布预测方法 —— 增长系数法 | 平均增长率法、底特律法、福莱特法&#xff0c;有不少同学私信我询问关于如何在 matlab 中调用函数、拆分代码以及需要大量调用的问题。于是我便想着把内容做一些优化&#xff0c;放在这篇文…

【MySQL】MySQL从0到0.9 - 持续更新ing

MySQL SQL 基础 DDL 语句 show databases; show tables; desc table_name; # 获取表信息 show create table 表名; // 查询指定表的建表语句 数据类型 char(10) 不满10个会用空格填充&#xff0c;性能好一点 varchar(10) 变长字符串&#xff0c;性能差一点 CREATE TABLE tabl…

nginx设置缓存时间

一、设置缓存时间 当网页数据返回给客户端后&#xff0c;可针对静态网页设置缓存时间&#xff0c;在配置文件内的http段内server段添加location&#xff0c;更改字段expires 1d来实现&#xff1a;避免重复请求&#xff0c;加快访问速度 第一步&#xff1a;修改主配置文件 #修…

[NOIP2011 普及组] 数字反转

AC代码&#xff1a; #include<iostream>using namespace std;int main() {long long n;cin >> n;long long temp n;long long sum 0;while(temp ! 0){int c temp % 10;sum sum * 10 c;temp temp / 10;}printf("%lld",sum);return 0; }

Spring 中 ApplicationContext 和 BeanFactory 的区别有哪些

先看一张类图&#xff1a; 区别&#xff1a; 1&#xff1a;包目录不同&#xff1a; spring-beans.jar 中 org.springframework.beans.factory.BeanFactory spring-context.jar 中 org.springframework.context.ApplicationContext 2&#xff1a;国际化&#xff1a; BeanFacto…

APP被针对攻击了,要怎么解决

随着APP行业的兴起&#xff0c;游戏公司异军突起&#xff0c;不管是在控证还是攻击方面都是属于最复杂的一个场面&#xff0c;游戏APP逐渐成为DDOS流量攻击的“重灾区”。没有提前做好了解就盲目进军游戏APP行业&#xff0c;一旦被攻击就会让公司束手无策。那么&#xff0c;刚上…

Python从入门到精通指南【第101篇—入门到精通】【文末送书-24】

文章目录 Python从入门到精通指南第一步&#xff1a;入门基础1.1 安装Python1.2 Hello World1.3 变量和数据类型1.4 控制流程 第二步&#xff1a;深入学习2.1 函数和模块2.2 列表、元组和字典2.3 文件操作 第三步&#xff1a;高级主题3.1 面向对象编程3.2 异常处理3.3 正则表达…

python_pyecharts绘制漏斗图

python-pyecharts绘制漏斗图 from pyecharts.charts import Funnel from pyecharts import options as opts# 数据 data [("访问", 100), ("咨询", 80), ("订单", 60), ("点击", 40), ("展现", 20)]# 创建漏斗图 funnel …

Qt OpenGL程序在Windows下正常,但在Linux下无显示问题【已解决】

Qt OpenGL程序在Windows下正常&#xff0c;但在Linux下无显示问题【已解决】 引言一、问题描述二、解决方案三、解决过程记录3.1 定位问题3.2 解决问题&#xff0c;深入分析 引言 在Windows上正常运行的OpenGL程序&#xff0c;到Linux下正常编译…但是没有任何显示(只有背景颜…

【数据结构与算法】动态规划法解题20240227

动态规划法 一、什么是动态规划二、动态规划的解题步骤三、509. 斐波那契数1、动规五部曲&#xff1a; 四、70. 爬楼梯1、动规五部曲&#xff1a; 五、746. 使用最小花费爬楼梯1、动规五部曲&#xff1a; 一、什么是动态规划 动态规划&#xff0c;英文&#xff1a;Dynamic Pro…