C++|哈希结构封装unordered_set和unordered_map

news2024/11/15 11:17:14

上一篇章,学习了unordered系列容器的使用,以及哈希结构,那么这一篇章将通过哈希结构来封装unordered系列容器,来进一步的学习他们的使用以及理解为何是如此使用。其实,哈希表的封装方式和红黑树的封装方式形式上是差不多的,如果有了红黑树的封装理解经验,我相信在理解哈希封装的过程会减少负担的。当然,在使用哈希结构中采用的是更具代表的哈希桶,接下来进行封装。

一、改造哈希表

1.1模板参数列表的改造

同理模板参数列表的改造跟红黑树的改造差不多。

K:关键码类型

V:不同容器v的容器类型不同,如果是unordered_map,v代表一个键值对,如果是unordered_set,v为k

KeyOfT:因为v的类型不同,通过keyOfT取key的方式就不同 。

Hash:哈希函数仿函数对象类型,哈希函数使用除留余数法,需要将字符串类型转化为整形数字才能取模。

1.2增加哈希表迭代器操作

1.2.1哈希表迭代器框架

    //前置声明,迭代器中才能定义HashTable对象
	template<class K, class T, class KeyOfT, class hash>
	class HashTable;

	template<class K,class T,class Ref,class Ptr,class KeyOfT,class hash = HashFunc<K>>
	struct _HTIterator
	{
		typedef _HTIterator<K,T, Ref, Ptr,KeyOfT,hash> Self;//方便作为接收迭代器操作的返回值的类型
		typedef HashNode<T> Node;
		typedef Node* PNode;

		PNode _node;//定义节点指针,方便访问其成员来完成迭代器的操作
		HashTable<K, T, KeyOfT, hash>* _ht;//定义哈希表指针,方便使用其成员来完成迭代器的操作
		size_t _hashi;//记录迭代器访问的位置

		_HTIterator(PNode node, HashTable<K, T, KeyOfT, hash>* ht,size_t hashi)
			:_node(node)
			,_ht(ht)
			,_hashi(hashi)
		{}

        //....
    };

1.2.2operator*() && operator->()

    	Ref operator*()
		{
			return _node->_data;
		}

		Ptr operator->()
		{
			return &(_node->_data);
		}

1.2.3operator++()

        Self& operator++()
		{
			if (_node->_next)//下一个元素不为空
			{
				_node = _node->_next;
			}
			else
			{
				/*hash hs;
				KeyOfT oft;
				int hashi = hs(oft(this->_data) % _tables.size());*/
                //寻找下一个不为空的桶
				++_hashi;
				while(_hashi < _ht->_tables.size())
				{
					if (_ht->_tables[_hashi] != nullptr)
					{
						_node = _ht->_tables[_hashi];
						break;
					}
					++_hashi;
				}
                //到末尾还没有找到不为空的桶
				if(_hashi == _ht->_tables.size())
					_node = nullptr;
				
			}
			return *this;

		}

1.2.4operator--()

        Self operator--()
		{
			PNode cur = _ht->_tables[_hashi];
			
			//当前节点就是第一个节点
			if (cur->_next == nullptr)
			{
				//寻找上一个非空桶
				--_hashi;
				while (_hashi)
				{
					//寻找该非空桶的最后一个元素
					if (_ht->_tables[_hashi])
					{
						cur = _ht->_tables[_hashi];
						while (cur->_next)
						{
							cur = cur->_next;
						}
						_node = cur;
						break;
					}
					_hashi--;
				}

			}
			else
			{
				while (cur->_next != _node)
				{
					cur = cur->_next;
				}
				_node = cur;
			}
            return *this;
		}

1.2.5operator==() && operator!=()

		bool operator==(const Self& x)
		{
			return _node == x._node;//由于没有重复的元素,直接比较节点的地址
		}

		bool operator!=(const Self& x)
		{

			return _node != x._node;
		}

二、如何用哈希表搭配unordered_map和unordered_set(仿函数)

我们可以用两张哈希表分别封装一份unordered_map和一份unordered_set,但是这样做的效果就带来了代码冗余。为了减少代码冗余,模拟跟库保持用一张哈希表封装unordered_map和unordered_set,但是该如何做到套用一张表呢,我们来进一步分析。

 首先对于unordered_map而言,其存放的节点值是pair,而对于unordered_set存放的是key,这对于哈希表节点的实现到是没啥问题,但是对于哈希表内部的构造,是需要查询插入的位置,就需要进行比较,若将比较实现成key的比较,那么对于pair类型又该如何比较,虽然知道比较的也是pair中的key,但是如何做到既满足unordered_set中的key类型比较,又满足pair类型中的key比较,总不能干两份代码吧。这个时候,我们的仿函数又派上用场了,对于unordered_set和unordered_map中都构造一个仿函数,分别表示取到unordered_set的key,和unordered_map中pair中的key,那么哈希表中的比较,就可以换成仿函数的比较,当往unordered_set中插入元素进行比较,调用的就是unordered_set的仿函数,当往unordered_map中插入元素进行比较,调用的就是unordered_map的仿函数从而达到回调。用一张图来进行表示,如图:

三、哈希表封装unordered_map和unordered_set(简易版)

3.1哈希表的实现(HashTable.h)

//哈希桶/链地址法
namespace Hash_Bucket
{
	template<class T>
	struct HashNode
	{
		HashNode(const T& data)
			:_next(nullptr)
			,_data(data)
		{}
		HashNode<T>* _next;
		T _data;
	};

	//前置声明,迭代器中才能定义HashTable对象
	template<class K, class T, class KeyOfT, class hash>
	class HashTable;

	template<class K,class T,class Ref,class Ptr,class KeyOfT,class hash = HashFunc<K>>
	struct _HTIterator
	{
		typedef _HTIterator<K,T, Ref, Ptr,KeyOfT,hash> Self;
		typedef HashNode<T> Node;
		typedef Node* PNode;

		PNode _node;
		HashTable<K, T, KeyOfT, hash>* _ht;
		size_t _hashi;
		_HTIterator(PNode node, HashTable<K, T, KeyOfT, hash>* ht,size_t hashi)
			:_node(node)
			,_ht(ht)
			,_hashi(hashi)
		{}

		Self& operator++()
		{
			if (_node->_next)
			{
				_node = _node->_next;
			}
			else
			{
				/*hash hs;
				KeyOfT oft;
				int hashi = hs(oft(this->_data) % _tables.size());*/

				++_hashi;
				while(_hashi < _ht->_tables.size())
				{
					if (_ht->_tables[_hashi] != nullptr)
					{
						_node = _ht->_tables[_hashi];
						break;
					}
					++_hashi;
				}
				if(_hashi == _ht->_tables.size())
					_node = nullptr;
				
			}
			return *this;

		}



		Self operator--()
		{
			PNode cur = _ht->_tables[_hashi];
			
			//当前节点就是第一个节点
			if (cur->_next == nullptr)
			{
				//寻找上一个非空桶
				--_hashi;
				while (_hashi)
				{
					//寻找该非空桶的最后一个元素
					if (_ht->_tables[_hashi])
					{
						cur = _ht->_tables[_hashi];
						while (cur->_next)
						{
							cur = cur->_next;
						}
						_node = cur;
						break;
					}
					_hashi--;
				}

			}
			else
			{
				while (cur->_next != _node)
				{
					cur = cur->_next;
				}
				_node = cur;
			}
			return *this;
		}

		Ref operator*()
		{
			return _node->_data;
		}

		Ptr operator->()
		{
			return &(_node->_data);
		}

		bool operator==(const Self& x)
		{
			return _node == x._node;//由于没有重复的元素,直接比较节点的地址
		}

		bool operator!=(const Self& x)
		{

			return _node != x._node;
		}

	};

	template<class K,class T,class KeyOfT,class hash>
	class HashTable
	{
		//声明友元,迭代器方可访问该类中的私有成员
		template<class K, class T, class Ref, class Ptr, class KeyOfT,class hash>
		friend struct _HTIterator;
	public:
		typedef _HTIterator<K, T,T&, T*, KeyOfT,hash> iterator;
		typedef _HTIterator<K, T, const T&, const T*, KeyOfT, hash> const_iterator;

		typedef HashNode<T> Node;
		typedef Node* PNode;

		HashTable(size_t size = 10)
		{
			_tables.resize(size);
		}

		iterator begin()
		{
			for (int i = 0; i < _tables.size(); i++)
			{
				if (_tables[i] != nullptr)
					return iterator(_tables[i], this, i);
			}
			return end();

		}
		iterator end()
		{
			return iterator(nullptr,this,-1);
		}

		const_iterator begin() const
		{
			for (int i = 0; i < _tables.size(); i++)
			{
				if (_tables[i] != nullptr)
					return iterator(_tables[i], this, i);
			}
			return end();

		}
		const_iterator end() const
		{
			return iterator(nullptr, this, -1);
		}

		iterator Find(const K& key)
		{
			KeyOfT oft;
			hash hs;
			int hashi = hs(key) % _tables.size();
			
			
			PNode cur = _tables[hashi];
			while (cur)
			{
				if(hs(oft(cur->_data)) == hs(key))
					return iterator(cur, this, hashi);

				cur = cur->_next;
			}
			return iterator(nullptr, this, -1);
		}

		pair<iterator,bool> Insert(T data)
		{
			hash hs;
			KeyOfT oft;
			iterator it = Find(oft(data));
			if (it != end())
				return make_pair(it,false);



			//扩容
			if (_n == _tables.size())
			{
				vector<PNode> _newHT(_tables.size()*2);
				
				for (int i = 0; i < _tables.size(); i++)
				{
					
					PNode cur = _tables[i];
					while (cur)
					{
						PNode next = cur->_next;

						int hashi = hs(oft(_tables[i]->_data)) % _newHT.size();

						//元素一直在变,所以不能用_tables[i]做代表
						/*_tables[i]->_next = nullptr;
						_newHT[hashi] = _tables[i];*/

						cur->_next = nullptr;
						_newHT[hashi] = cur;
						
						cur = next;
					}
					_tables[i] = nullptr;
				}
                _tables.swap(_newHT);
			}
			//头插
			PNode newnode =new Node(data);
			int hashi = hs(oft(data)) % _tables.size();
			newnode->_next = _tables[hashi];
			_tables[hashi] = newnode;
			++_n;
			
			return make_pair(iterator(newnode, this, hashi),true);

		}

		bool Erase(const K& key)
		{
			KeyOfT oft;
			iterator it = Find(key);
			if (it == end())
				return false;
			hash hs;
			int hashi = hs(key) % _tables.size();
			PNode cur = _tables[hashi];
			PNode parent = nullptr;
			while (hs(oft(cur->_data)) != hs(key))
			{
				parent = cur;
				cur = cur->_next;
			}
			if (parent)
			{
				delete cur;
				parent->_next = nullptr;
			}
			else
			{
				delete cur;
				_tables[hashi] = nullptr;
			}
			return true;
		}

		~HashTable()
		{
			for (int i = 0; i < _tables.size(); i++)
			{
				PNode cur = _tables[i];
				while (cur)
				{
					PNode next = cur->_next;

					delete cur;

					cur = next;
				}
				_tables[i] = nullptr;
			}
		}



		//另外加的,为了测试用
		void Some()
		{
			size_t bucketSize = 0;
			size_t maxBucketLen = 0;
			size_t sum = 0;
			double averageBucketLen = 0;

			for (size_t i = 0; i < _tables.size(); i++)
			{
				Node* cur = _tables[i];
				if (cur)
				{
					++bucketSize;
				}

				size_t bucketLen = 0;
				while (cur)
				{
					++bucketLen;
					cur = cur->_next;
				}

				sum += bucketLen;

				if (bucketLen > maxBucketLen)
				{
					maxBucketLen = bucketLen;
				}
			}

			averageBucketLen = (double)sum / (double)bucketSize;

			printf("all bucketSize:%d\n", _tables.size());
			printf("bucketSize:%d\n", bucketSize);
			printf("maxBucketLen:%d\n", maxBucketLen);
			printf("averageBucketLen:%lf\n\n", averageBucketLen);
		}
	private:
		vector<HashNode<T>*> _tables;
		size_t _n = 0;
	};

}

3.2unordered_map的模拟实现(My_Unordered_Map.h)

#pragma once

#include "HashTable.h"


namespace Hash_Bucket
{
	template<class K,class V,class Hash = HashFunc<K>>
	class unordered_map
	{
	public:
		struct KeyOfTMap
		{
			const K& operator()(const pair<const K, V>& data)
			{
				return data.first;
			}
		};
		typedef typename HashTable<K, pair<const K,V>, KeyOfTMap, Hash>::iterator iterator;
		typedef typename HashTable<K, pair<const K,V>, KeyOfTMap, Hash>::const_iterator const_iterator;


		iterator begin()
		{
			return _hs.begin();
		}
		iterator end()
		{
			return _hs.end();
		}

		const_iterator begin() const
		{
			return _hs.begin();
		}
		const_iterator end() const
		{
			return _hs.end();
		}

		pair<iterator, bool> insert(const pair<K,V>& data)
		{
			return _hs.Insert(data);
		}
		V& operator[](const K& key)
		{
			pair<iterator, bool> ret = _hs.Insert(make_pair(key,V()));
			return ret.first->second;
		}

		bool erase(const K& key)
		{
			return _hs.Erase(key);
		}

		iterator find(const K& key)
		{
			return _hs.Find(key);
		}

	private:
		HashTable<K, pair<const K,V>, KeyOfTMap, Hash> _hs;
	};

	void test_map()
	{
		unordered_map<string, string> dict;
		dict.insert(make_pair("sort", ""));
		dict.insert(make_pair("string", ""));
		dict.insert(make_pair("insert", ""));

		for (auto& kv : dict)
		{
			//kv.first += 'x';
			kv.second += 'x';

			cout << kv.first << ":" << kv.second << endl;
		}
		cout << endl;

		string arr[] = { "你", "  ","好", "  ", "吗", "  ", "你", "吃", "  ", "了", "饭", "吗", "?" };
		unordered_map<string, int> count_map;
		for (auto& e : arr)
		{
			count_map[e]++;
		}

		for (auto& kv : count_map)
		{
			cout << kv.first << ":" << kv.second << endl;
		}
		cout << endl;
	}
}

3.3unordered_set的模拟实现(My_Unordered_Set.h)

#pragma once

#include "HashTable.h"


namespace Hash_Bucket
{
	template<class K,class Hash = HashFunc<K>>
	class unordered_set
	{
	public:
		struct KeyOfTSet
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
		typedef typename HashTable<K, const K, KeyOfTSet, Hash>::iterator iterator;
		typedef typename HashTable<K, const K, KeyOfTSet, Hash>::const_iterator const_iterator;


		iterator begin()
		{
			return _hs.begin();
		}
		iterator end()
		{
			return _hs.end();
		}

		const_iterator begin() const
		{
			return _hs.begin();
		}
		const_iterator end() const
		{
			return _hs.end();
		}
		
		pair<iterator, bool> insert(const K& key)
		{
			return _hs.Insert(key);
		}


		bool erase(const K& key)
		{
			return _hs.Erase(key);
		}

		iterator find(const K& key)
		{
			return _hs.Find(key);
		}
		void some()
		{
			_hs.Some();
		}
	private:
		HashTable<K, const K, KeyOfTSet, Hash> _hs;
	};

	void test_set()
	{
		
		unordered_set<int> us;
		us.insert(5);
		us.insert(15);
		us.insert(52);
		us.insert(3);

		unordered_set<int>::iterator it = us.begin();
		while (it != us.end())
		{
			//*it += 5;
			cout << *it << " ";
			++it;
		}
		cout << endl;

		for (auto e : us)
		{
			cout << e << " ";
		}
		cout << endl;
		us.some();
	}

}

3.4测试(test.cpp)

#include "My_Unordered_Map.h"
#include "My_Unordered_Set.h"

int main()
{

	Hash_Bucket::test_map();
	Hash_Bucket::test_set();

	return 0;
}

输出结果:

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

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

相关文章

鸿蒙低代码开发的局限性

在版本是DevEco Studio 3.1.1 Release&#xff0c;SDK是3.1.0(API9) 的基础上。 1、低代码插件没有WebView组件。 2、低代码插件没有空白的自定义组件&#xff0c;当前提供的所谓自定义组件&#xff0c;只能用列表中提供的组件来拼接新的组件。 3、使用ets代码自定义的组件&…

JVM 常量池汇总

Tips JVM常量池分为静态常量池和运行时常量池&#xff0c;因为Jdk1.7后字符串常量池从运行时常量池存储位置剥离&#xff0c;故很多博客也是区分开来&#xff0c;存储位置和内容注意区别&#xff01; 字符串常量池底层是由C实现&#xff0c;是一个类似于HashTable的数据结构&am…

Spring 中使用MyBatis

一、Mybatis 的作用 1、MyBatis&#xff08;前身为iBatis&#xff09;是一个开源的Java持久层框架&#xff0c;它主要用于与数据库交互&#xff0c;帮助开发者更轻松地进行数据库操作。 持久层&#xff1a;指的是就是数据访问层(dao)&#xff0c;是用来操作数据库的。 2、MyB…

Filament 【表单操作】修改密码

场景描述&#xff1a; 新增管理员信息时需要填写密码&#xff0c;修改管理员信息时密码可以为空&#xff08;不修改密码&#xff09;&#xff0c;此时表单中密码输入有冲突&#xff0c;需要对表单中密码字段进项条件性的判断&#xff0c;使字段在 create 操作时为必需填写&…

深度学习-注意力机制和分数

深度学习-注意力机制 注意力机制定义与起源原理与特点分类应用领域实现方式优点注意力机制的变体总结注意力分数定义计算方式注意力分数的作用注意力分数的设计总结 注意力机制&#xff08;Attention Mechanism&#xff09;是一个源自对人类视觉研究的概念&#xff0c;现已广泛…

实测 WordPress 最佳优化方案:WP Super Cache+Memcached+CDN

说起 WordPress 优化加速来可以说是个经久不衰的话题了&#xff0c;包括明月自己都撰写发表了不少相关的文章。基本上到现在为止明月的 WordPress 优化方案已经固定成型了&#xff0c;那就是 WP Super CacheMemcachedCDN 的方案&#xff0c;因为这个方案可以做到免费、稳定、安…

如何用R语言ggplot2画高水平期刊散点图

文章目录 前言一、数据集二、ggplot2画图1、全部代码2、细节拆分1&#xff09;导包2&#xff09;创建图形对象3&#xff09;主题设置4&#xff09;轴设置5&#xff09;图例设置6&#xff09;散点颜色7&#xff09;保存图片 前言 一、数据集 数据下载链接见文章顶部 处理前的数据…

基于FreeRTOS+STM32CubeMX+LCD1602+MCP6S26(SPI接口)的6通道模拟可编程增益放大器Proteus仿真

一、简介: MCP6S26是模拟可 编程增益放大器(Programmable Gain Amplifiers, PGA)。它们可配置为输出 +1 V/V 到 +32 V/V 之间的增 益,输入复用器可通过 SPI 端口选择最多 6 个通道中的 一个。串行接口也可以将 PGA 置为关断模式,以降低 功耗。这些 PGA 针对高速度、低失调…

Python编程基础5

邮件编程 SMTP&#xff08;Simple Mail Transfer Protocol&#xff09;简单邮件传输协议&#xff0c;使用TCP协议25端口&#xff0c;它是一组用于由源地址到目的地址传送邮件的规则&#xff0c;由它来控制信件的中转方式。python的smtplib提供了一种很方便的途径发送电子邮件。…

【python】tkinter GUI开发: Button和Entry的应用实战探索

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

MySQL----排序ORDER BY

在对数据进行处理的时候&#xff0c;我们通常需要对读取的数据进行排序。而 MySQL 的也提供了 ORDER BY 语句来满足我们的排序要求。 ORDER BY 可以按照一个或多个列的值进行升序&#xff08;ASC&#xff09;或降序&#xff08;DESC&#xff09;排序。 语法 SELECT column1…

航班进出港管理系统的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;管理员管理&#xff0c;用户管理&#xff0c;航班信息管理&#xff0c;航飞降落请求管理&#xff0c;公告信息管理 前台账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;公告信息&a…

中国新兴的数字证书品牌——JoySSL

JoySSL是一个基于全球可信顶级根创新推出的新一代https数字证书&#xff0c;也是中国为数不多的自主品牌SSL证书。以下是关于JoySSL的详细介绍&#xff1a; 1 品牌背景&#xff1a; JoySSL是网盾安全旗下的产品&#xff0c;专注于网络安全技术服务、安全防护系统集成、数据安…

计算机网络知识CIDR(无类别域区间路由)

目录 介绍 基本信息 优点与关联 如何计算判定范围&#xff08;你应该是来看这个的&#xff0c;前面是水字数的&#xff09; 省流版 介绍 无类别域间路由&#xff08;Classless Inter-Domain Routing、CIDR&#xff09;是一个用于给用户分配IP地址以及在互联网上有效地路由…

【SkiaSharp绘图】01使用SkiaSharp绘制Hello World

文章目录 SkiaSharp简介主要特点适用场景 SkiaSharp的Hello World!1、创建一个.NET Framework项目2、拖入SKGLControl控件GLControl、SKControl、SKGLControl对比GLControlSKControlSKGLControl选择和使用场景高性能的选择 SKGLControl 3、绘制Hello World 为何选择SkiaSharpIm…

绘唐科技官网

绘唐科技AI工具是一系列经过训练的人工智能工具&#xff0c;旨在提供各种智能化的解决方案。这些工具可以应用于多个领域&#xff0c;包括自然语言处理、图像识别、语音识别、机器学习等。 其中&#xff0c;自然语言处理工具可以帮助用户处理和理解文本数据。它可以实现文本分类…

150W无感功率电阻器

特点&#xff1a;1x150 W/2x60 W/3x33 W功率 SOT-227模具封装两种不同的端子选项 可供选择良好的机械可靠性 无感设计 电阻值&#xff1a;0.5Ω≤ 1MΩ 耐抗精度:1%- 10 % 温度系数:50 ppm/℃- 250 ppm/℃(25℃-105℃&#xff09; 额定功率:150W 70℃ bottom case tempe…

Thinkphp一文鸡富贵鸡玫瑰庄园富农场仿皮皮果理财农场源码

Thinkphp一文鸡富贵鸡玫瑰庄园富农场仿皮皮果理财农场源码&#xff0c;喜欢的朋友可以下载研究 一文鸡富贵鸡玫瑰庄园富农场仿皮皮果理财农场源码

8-1RT-Thread消息队列

8-1RT-Thread消息队列 消息队列又称队列&#xff0c;是一种常用于线程间通信的数据结构。 消息队列控制块里有两个链表&#xff0c;空闲列表用来挂接空的小几块&#xff0c;另一个链表是用来挂接存有消息的消息框。其中消息链表头指向消息队列当中的第一个消息框&#xff0c;而…

Python 制作词云图

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…