【C++】哈希对unordered_map和unodered_set的封装

news2025/1/16 13:56:46

在这里插入图片描述

🚀write in front🚀
📜所属专栏: C++学习
🛰️博客主页:睿睿的博客主页
🛰️代码仓库:🎉VS2022_C语言仓库
🎡您的点赞、关注、收藏、评论,是对我最大的激励和支持!!!
关注我,关注我,关注我你们将会看到更多的优质内容!!

在这里插入图片描述

文章目录

  • 前言
  • 一.哈希表的修改
  • 二.封装map和set
  • 三.普通迭代器
  • 四.const迭代器
  • 五.insert返回值,operator[]和key不能修改的问题
  • 总结

前言

  在前面的学习里面,我们完成了红黑树对map和set的封装,今天我们就用哈希里面的开散列对unordered_map和unodered_set进行封装。其实哈希的封装和红黑树的封装是非常相像的,所以我们这里简单道来。

一.哈希表的修改

  和红黑树一样,我们把模板参数改成:

template<class K,class T,class KeyOfT,class HashFunc = DefaultHashFunc<K>>
class HashTable
{}

K:关键码类型
T:对于unordered_map,T就是有个键值对;对于unordered_set,T就是Key。
keyOfT:取出元素(主要是为unordered_map设计)
HashFunc:仿函数,将key转换成整数,才能进行取模。
结点改为:

template<class T>
	struct HashNode
	{
		T _data;
		HashNode<T>* _next=nullptr;

		HashNode(const T& data):_data(data)
		{}
	};

二.封装map和set

对于map和set,我们也是直接修改一下模板传过来的值就可以了。

三.普通迭代器

template<class K, class T,class Ptr,class Ref, class KeyOfT, class  HashFunc>
	struct HTIterator
	{
	public:

		typedef HashNode<T> Node;
		typedef HTIterator<K, T,Ptr,Ref, KeyOfT, HashFunc> Self;
		typedef HTIterator<K, T, T*, T&, KeyOfT, HashFunc> iterator;

		Node* _node;
		HashTable <K, T, KeyOfT, HashFunc>* _pht;
		HTIterator(Node* node, HashTable <K,T,KeyOfT, HashFunc>* pht) :_node(node),_pht(pht)
		{}

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

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

		bool operator!=(const Self& t)
		{
			return _node != t._node;
		}
	};

在这里对于迭代器的++操作,要寻找下一个元素就有两种情况,链表的下一个或者下一个哈希桶,所以这里为了找到下一个哈希桶,就要把哈希传过来,所以这里要传哈希的指针,为了使迭代器可以写出哈希的指针,这里的迭代器的模板参数也要有keyofThashfunc

	Self& operator++()
		{
			if (_node->_next)
			{
				_node = _node->_next;
			}
			else
			{
				KeyOfT kot;
				HashFunc hf;
				size_t hashi = hf(kot(_node->_data)) % _pht->_table.size();
				hashi++;
				while (hashi < _pht->_table.size())
				{
					if (_pht->_table[hashi])
					{
						_node = _pht->_table[hashi];
						return *this;
					}
					hashi++;
				}

					_node = nullptr;
			}

			return *this;
		}

四.const迭代器

  随后我们就可以构造出const迭代器了。随后我们就要写出用普通迭代器构造const迭代器的构造函数。并且这里要在hashtable之前使用hashtable,就要先申明一下:

//这里就别写= DefaultHashFunc<K> 了,不然会保错:重定义参数
	template<class K, class T, class KeyOfT, class  HashFunc>
	class HashTable;
	template<class K, class T,class Ptr,class Ref, class KeyOfT, class  HashFunc>
	struct HTIterator
	{
	public:

		typedef HashNode<T> Node;
		typedef HTIterator<K, T,Ptr,Ref, KeyOfT, HashFunc> Self;
		typedef HTIterator<K, T, T*, T&, KeyOfT, HashFunc> iterator;

		Node* _node;

		//这里如果传过来的是const类型的pht,权限会放大,所以说要改成下面的情况:
		//HashTable <K, T, KeyOfT, HashFunc>* _pht;
		/*HTIterator(Node* node, HashTable <K,T,KeyOfT, HashFunc>* pht) :_node(node),_pht(pht)
		{}*/

		//因为在这里我们对于这个哈希桶不会进行修改,所以直接用const修饰,这样只会出现权限的缩小或平移
		const HashTable <K, T, KeyOfT, HashFunc>* _pht;
		HTIterator(Node* node, const HashTable <K, T, KeyOfT, HashFunc>* pht) :_node(node), _pht(pht)
		{}

		HTIterator(const iterator& it) :_node(it._node), _pht(it._pht)
		{}

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

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

		Self& operator++()
		{
			if (_node->_next)
			{
				_node = _node->_next;
			}
			else
			{
				KeyOfT kot;
				HashFunc hf;
				size_t hashi = hf(kot(_node->_data)) % _pht->_table.size();
				hashi++;
				while (hashi < _pht->_table.size())
				{
					if (_pht->_table[hashi])
					{
						_node = _pht->_table[hashi];
						return *this;
					}
					hashi++;
				}

					_node = nullptr;
			}

			return *this;
		}

		bool operator!=(const Self& t)
		{
			return _node != t._node;
		}
	};

五.insert返回值,operator[]和key不能修改的问题

  这里和红黑树封装map和set类似,就不用多说了。下面看看完整代码:

#pragma once
#include<vector>
#include<iostream>
using namespace std;


template<class T>
class DefaultHashFunc
{
public:
	size_t operator()(const T& key)
	{
		return (size_t)key;
	}
};

template<>
class DefaultHashFunc<string>
{
public:
	size_t operator()(const string& key)
	{
		size_t value = 1;
		for (auto& e : key)
		{
			value *= 131;
			value += e;
		}
		return value;
	}
};
namespace hash_bucket
{
	template<class T>
	struct HashNode
	{
		T _data;
		HashNode<T>* _next=nullptr;

		HashNode(const T& data):_data(data)
		{}
	};


	//这里就别写== DefaultHashFunc<K> 了,不然会保错:重定义参数
	template<class K, class T, class KeyOfT, class  HashFunc >
	class HashTable;

	template<class K, class T,class Ptr,class Ref, class KeyOfT, class  HashFunc>
	struct HTIterator
	{
	public:

		typedef HashNode<T> Node;
		typedef HTIterator<K, T,Ptr,Ref, KeyOfT, HashFunc> Self;
		typedef HTIterator<K, T, T*, T&, KeyOfT, HashFunc> iterator;

		Node* _node;

		//这里如果传过来的是const类型的pht,权限会放大,所以说要改成下面的情况:
		//HashTable <K, T, KeyOfT, HashFunc>* _pht;
		/*HTIterator(Node* node, HashTable <K,T,KeyOfT, HashFunc>* pht) :_node(node),_pht(pht)
		{}*/

		//因为在这里我们对于这个哈希桶不会进行修改,所以直接用const修饰,这样只会出现权限的缩小或平移
		const HashTable <K, T, KeyOfT, HashFunc>* _pht;
		HTIterator(Node* node, const HashTable <K, T, KeyOfT, HashFunc>* pht) :_node(node), _pht(pht)
		{}

		HTIterator(const iterator& it) :_node(it._node), _pht(it._pht)
		{}

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

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

		Self& operator++()
		{
			if (_node->_next)
			{
				_node = _node->_next;
			}
			else
			{
				KeyOfT kot;
				HashFunc hf;
				size_t hashi = hf(kot(_node->_data)) % _pht->_table.size();
				hashi++;
				while (hashi < _pht->_table.size())
				{
					if (_pht->_table[hashi])
					{
						_node = _pht->_table[hashi];
						return *this;
					}
					hashi++;
				}

					_node = nullptr;
			}

			return *this;
		}

		bool operator!=(const Self& t)
		{
			return _node != t._node;
		}
	};

	template<class K,class T, class KeyOfT,class HashFunc= DefaultHashFunc<K> >
	class HashTable
	{
		typedef HashNode<T> Node;
		//友员声明
		template<class K, class T, class Ptr,class Ref,class KeyOfT, class HashFunc>
		friend struct HTIterator;
	public:
		HashTable()
		{
			_table.resize(10,nullptr);
		}

		~HashTable()
		{
			for (int i = 0; i < _table.size(); i++)
			{
				Node* cur = _table[i];
				while (cur)
				{
					Node* next = cur->_next;
					delete cur;
					cur = next;
					
				}

				_table[i] = nullptr;
			}
		}

		typedef HTIterator<K, T,T*,T&, KeyOfT, HashFunc>  iterator;
		typedef HTIterator<K, T, const T*, const T&, KeyOfT, HashFunc>  const_iterator;


		iterator begin()
		{
			for (int 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 (int 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);
		}

		pair<iterator,bool> Insert(const T& data)
		{
			KeyOfT kot;
			auto it = Find(kot(data));
			if (it!=end())
			{
				return make_pair(it,true);
			}
			HashFunc hf;
			if (_table.size() == _n)
			{
				//扩容

				HashTable newhash;
				size_t newsize = _table.size() * 2;
				newhash._table.resize(newsize);
				for (int 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 = newhash._table[i];
						newhash._table[i] = cur;

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

				_table.swap(newhash._table);
			}
			
			size_t hashi = hf(kot(data)) % _table.size();
			Node* cur = new Node(data);
			cur->_next = _table[hashi];
			_table[hashi] = cur;
			_n++;

			return make_pair(iterator(cur,this),false);
			
		}



		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();
		}

		bool Erase(const K& key)
		{
			HashFunc hf;
			KeyOfT kot;
			size_t hashi = hf(key) % _table.size();
			Node* cur = _table[hashi];
			Node* prev = nullptr;
			while (cur)
			{
				
				if (kot(cur->_data) ==key)
				{
					if (prev == nullptr)
					{
						_table[hashi] = cur->_next;
					}
					else
					{
						prev->_next = cur->_next;
					}
					delete cur;
					cur = nullptr;
					return true;
				}

				prev = cur;
				cur = cur->_next;
			}

			return false;

			
		}

		void Print()
		{
			for (int i = 0; i < _table.size(); i++)
			{
				printf("[%d]: ", i);
				Node* cur = _table[i];

				while (cur)
				{
					cout << "("<<cur->_kv.first << ":" << cur->_kv.second<<")" << " ->";
					cur = cur->_next;
				}
				printf("NULL\n");
			}
		}
	private:
		vector<Node*>  _table;
		size_t _n=0;
	};
}

set的封装:

namespace zxr
{
	template<class K>
	class unordered_set
	{
	private:
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
		hash_bucket::HashTable<K, K,SetKeyOfT> _ht;

	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&data)
		{
			pair<typename hash_bucket::HashTable<K, K, SetKeyOfT>::iterator, bool> it= _ht.Insert(data);
			return pair<iterator,bool>(it.first, it.second);
			
		}

		bool erase(const K& key)
		{
			return _ht.Erase(key);
		}
		bool find(const K& key)
		{
			return _ht.Find(key);
		}
	};
}

map的封装:

namespace zxr
{
	template<class K,class V>
	class unordered_map
	{
	private:
		struct MapKeyOfT
		{
			const K& operator()(const pair<const K,V>& kv)
			{
				return kv.first;
			}
		};
		hash_bucket::HashTable<K, pair<const K,V>, MapKeyOfT> _ht;

	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;
		iterator begin()
		{
			return _ht.begin();
		}
		iterator end()
		{
			return _ht.end();
		}

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

		pair<iterator,bool> insert(const pair<const K,V>& data)
		{
			return _ht.Insert(data);
		}

		V& operator [](const K& key)
		{
			auto it = insert(make_pair(key,V()));
			return it.first->second;
		}
		bool erase(const K& key)
		{
			return _ht.Erase(key);
		}
		bool find(const K& key)
		{
			return _ht.Find(key);
		}
	};
}

总结

在这里插入图片描述
  在这里我们在讲几个之前没理解的点,我们的迭代器在平时可以用const迭代器来介绍迭代器,就是因为库里面已经写了用普通迭代器构造const迭代器的构造函数,随意我们才能理所当然的使用。
这里要强调的还是,同一模板传了不同参数,此时就生成了两个不同的类型,无论模板参数是const还是普通,都不在是同一个类型了。
在这里插入图片描述

封装的过程都是这样的:
1、哈希表
2、封装map和set
3、普通迭代器
4、const迭代器
5、insert返回值 operator[]
6、key不能修改的问题

  更新不易,辛苦各位小伙伴们动动小手,👍三连走一走💕💕 ~ ~ ~ 你们真的对我很重要!最后,本文仍有许多不足之处,欢迎各位认真读完文章的小伙伴们随时私信交流、批评指正!

专栏订阅:
每日一题
C语言学习
算法
智力题
初阶数据结构
Linux学习
C++学习
更新不易,辛苦各位小伙伴们动动小手,👍三连走一走💕💕 ~ ~ ~ 你们真的对我很重要!最后,本文仍有许多不足之处,欢迎各位认真读完文章的小伙伴们随时私信交流、批评指正!

在这里插入图片描述

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

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

相关文章

第9期ThreadX视频教程:自制个微秒分辨率任务调度实现方案(2023-10-11)

视频教程汇总帖&#xff1a;【学以致用&#xff0c;授人以渔】2023视频教程汇总&#xff0c;DSP第12期&#xff0c;ThreadX第9期&#xff0c;BSP驱动第26期&#xff0c;USB实战第5期&#xff0c;GUI实战第3期&#xff08;2023-10-11&#xff09; - STM32F429 - 硬汉嵌入式论坛 …

“之江创客”聚焦农村电商创新发展 扎实助推共同富裕

10月10日下午&#xff0c;由商务部中国国际电子商务中心指导&#xff0c;浙江省商务厅等十个部门主办&#xff0c;浙江省电子商务促进中心联合丽水市商务局承办的“之江创客”2023全球电子商务创业创新大赛农村电商赛区决赛暨颁奖典礼在丽水圆满落幕。浙江省商务厅党组成员、副…

Folium笔记: Popup

1 介绍 在 folium 中&#xff0c;Popup 是一个用于在地图上显示附加信息的对象。当在地图上点击一个标记&#xff08;例如&#xff0c;一个点或者一个形状&#xff09;时&#xff0c;Popup 会显示出来。Popup 可以包含纯文本&#xff0c;但也可以包含HTML代码 2 主要参数 htm…

安捷伦E9326A/E9327A射频传感器

出售Agilent安捷伦E9326A/E9327A射频传感器 E9326A 是 Agilent 使用的 18 GHz 0.1 瓦射频传感器。电子测试设备传感器测量波形的功率&#xff0c;例如多音和调制射频 (RF) 波形。传感器使用二极管检测器收集高度精确的调制测量值。 1.5 MHz 视频带宽&#xff0c;非常适合蓝牙?…

Python算法练习 10.14

leetcode 2095 删除链表的中间节点 给你一个链表的头节点 head 。删除 链表的 中间节点 &#xff0c;并返回修改后的链表的头节点 head 。 长度为 n 链表的中间节点是从头数起第 ⌊n / 2⌋ 个节点&#xff08;下标从 0 开始&#xff09;&#xff0c;其中 ⌊x⌋ 表示小于或等于…

androidx和v4包资源冲突解决方法

一、资源包会报如下错误&#xff1a; 错误类似 (androidx.core:core:1.10.0) 和 (com.android.support:support-compat:24.2.0) 表示资源重复&#xff0c;不知调用androidx包下面的&#xff0c;还是v4包下面的 Duplicate class android.support.v4.app.INotificationSideCha…

恒温区检测热电偶

声明 本文是学习GB-T 4000-2017 焦炭反应性及反应后强度试验方法. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 7— 进气口&#xff1b; 8— 测温热电偶。 图 A.1 单点测温加热炉体结构示意图 A.3 温度控制装置 控制精度&#xff1a;(11003)℃。…

RK3568 DRM显示框架

一.简介 显示子系统是 Rockchip 平台显示输出相关软硬件系统的统称&#xff0c;它包括 VOP&#xff08;比较老的平台叫 LCDC&#xff0c;比如 RK3188、RK3066&#xff09;和 RGB、BT1120、BT656、I8080&#xff08;MCU 显示接口&#xff09;&#xff0c;LVDS、MIPI DSI、EDP、…

PyTorch模型INT8量化基础

PyTorch模型INT8量化基础 最基础的Tensor量化校准两种不同的量化方案每张量和每通道量化方案量化后端引擎配置QConfigTensor量化Post Training Static Quantization (训练后静态量化)fuse_model:融合网络中的一些层 设置qCONFIGprepare: 定标 &#xff1a;scale 和 zero_point喂…

ASEMI整流桥KBL410需要散热片吗?

编辑-Z 在决定电子设备或半导体组件的配置时&#xff0c;了解每个部件的性能和需求至关重要。那么&#xff0c;对于KBL410这款整流桥&#xff0c;它是否需要散热片呢&#xff1f;在本文中&#xff0c;我们将详细解析KBL410的工作原理&#xff0c;以及是否需要散热片。 首先&am…

数字电路中触发器/锁存器的简单理解,与电路结构

1&#xff0c;为什么有触发器? 数字逻辑电路系统包含组合逻辑和时序逻辑。组合逻辑用来实现与状态无关的门电路&#xff0c;比如算法的实现函数&#xff0c;无反馈&#xff0c;无记忆&#xff1b;时序逻辑则主要用来同步电路的各个状态&#xff0c;有反馈&#xff0c;有记忆&a…

Java多线程间的通信:生产者消费者问题

逻辑分析 代码实现 package ThreadCommunction;import sun.security.krb5.internal.crypto.Des;import java.util.Date;//目标&#xff1a;了解线程通信 public class ThreadTest {public static void main(String[] args) {//需求&#xff1a;3个生产者线程&#xff0c;负责产…

STM32F4X OLED使用

STM32F4X OLED使用 OLED简介OLED使用OLED驱动芯片SSD1309引脚SSD1309通信协议4线SPI模式3线SPI模式I2C模式OLED地址确认 8080接口OLED 8080写模式OLED 8080读模式 6800接口 SSD1309显示原理SSD1309显存大小SSD1309显存分布SSD1039数据显示MCU操作SSD1309显存方法 OLED取模字符取…

C++类总结

参考&#xff1a; C中的private, public, protected_c private-CSDN博客https://www.cnblogs.com/corineru/p/11001242.html C 中 Private、Public 和 Protected 的区别 Private Public Protected 声明为private类成员只能由基类内部的函数访问。 可以从任何地方访问声明…

Linux系统编程详解

Linux 多线程编程 什么是线程&#xff1f; 与线程类似&#xff0c;线程是允许应用程序并发执行多个任务的一种机制 线程是轻量级的进程&#xff08;LWP&#xff1a;Light Weight Process&#xff09;&#xff0c;在 Linux 环境下线程的本 质仍是进程。 一个进程可以包含多个线…

使用dumuz工具实现淘宝收藏的宝贝批量下载(批量导出)

淘宝买家在实际应用中经常会将关心的淘宝宝贝添加到淘宝的收藏夹里&#xff0c;方便稍后查看和购买。如果你希望将淘宝收藏夹中的内容导出来&#xff0c;以便自己进行归类整理&#xff0c;可以按照以下教程进行操作&#xff1a; 应用功能描述 模拟人工操作淘宝"收藏夹-&…

消息称苹果或在明年推出搭载M3芯片的MacBook产品

近日据 DigiTimes 发布的博文&#xff0c;苹果公司计划在 2024 年推出搭载 M3 芯片的 MacBook 产品。然而&#xff0c;关于这款新产品的发布日期仍存在争议。虽然一些爆料认为苹果可能会在今年发布这款产品&#xff0c;但也有一些爆料认为发布时间会推迟到 2024 年。根据各项报…

Vue3引入腾讯地图,点击坐标后实时获取经纬度

本文将介绍如何在Vue 引入腾讯地图组件&#xff0c;引入后可以直接在页面中渲染腾讯地图&#xff0c;实现 经纬度 与 地图锚点位置的双向绑定&#xff0c;如&#xff1a; 1&#xff0c;输入经纬度后&#xff0c;地图自动定位到指定位置&#xff1b;2&#xff0c;鼠标在地图点击…

[论文笔记]SimCSE

引言 今天带来一篇当时引起轰动的论文SimCSE笔记,论文题目是 语句嵌入的简单对比学习。 SimCSE是一个简单的对比学习框架,它可以通过无监督和有监督的方式来训练。 对于无监督方式,输入一个句子然后在一个对比目标中预测它自己,仅需要标准的Dropout作为噪声。这种简单的…

XXL-Job分布式任务调度框架-知识点汇总5

一 XXLlob 1.1 xxl-job作用 XXL-JOB是一个轻量级分布式任务调度平台&#xff0c;XXL-JOB主要提供了任务的动态配置管理、任务监控和统计报表以及调度日志几大功能模块&#xff0c;支持多种运行模式和路由策略&#xff0c;可基于对应执行器机器集群数量进行简单分片数据处理。…