[C++随想录] 哈希之unordered_map和unordered-set的封装

news2024/10/5 18:30:21

unordered_map和unordered_set的封装

  • 1. hash模版的改变
    • 1.1 hash类模板 头的改变
    • 1.2 封装迭代器类
      • 1.2.1 构造 && 拷贝构造
      • 1.2.2. ++
      • 1.2.3. 其他运算符重载
    • 1.3 hash类实现普通迭代器和const迭代器
  • 2. unordered_set的底层逻辑
  • 3. unordered_map的底层逻辑
  • 4. 源码
    • 4.1 hash类
    • 4.2 unordered_set类
    • 4.3 unordered_map类

1. hash模版的改变

1.1 hash类模板 头的改变


1.2 封装迭代器类

🗨️ 为什么要传那么多的参数? 为什么要有hash类的前置声明呢? 为什么要多一个哈希表指针成员变量呢?

  • 加加操作中, 虽然可以找到 桶号, 但是没有 哈希桶 是没有意义的 && 哈希表指针也是可以完成哈希桶的操作的 ⇒ 所以在 迭代器类中需要有一个哈希表指针
    由于要有哈希表指针 , 所以会用到 hash类的相关参数(T - 数据类型, KeyofT, Com - 比较逻辑) 和 相关函数(比如构造啥的)迭代器类要有足够多的参数 和 hash前置声明一下

1.2.1 构造 && 拷贝构造

  1. 构造
_iterator(Node* node,const hash<K, T, KeyofT, Com>* pht)
	:_node(node)
	, _pht(pht)
{}
  1. 用普通迭代器初始化const迭代器
_iterator(const Iterator& node)
	:_node(node._node)
	, _pht(node._pht)
{}

1.2.2. ++

有两种情况 :

  1. 当前桶还有数据 — — 返回当前桶当前位置的下一个元素即可
  2. 当前桶没有数据 — — 去找下一个有元素的桶
self& operator++()
{
	Com com;
	KeyofT kot;
	size_t hashi = com(kot(_node->_data)) % _pht->_table.size();
	
	// 当前桶还有节点
	if (_node->_next)
	{
		_node = _node->_next;
	}
	// 当前桶没有节点
	else
	{
		hashi++;

		while (hashi < _pht->_table.size())
		{
			// 找到了, 就返回
			if (_pht->_table[hashi])
			{
				_node = _pht->_table[hashi];
				return *this;
			}
			else
			{
				hashi++;
			}
		}
		
		// 说明后面的桶都没有元素了, 那就返回nullptr
		_node = nullptr;

	}

	return *this;
}

1.2.3. 其他运算符重载

  1. operator==
bool operator==(const self& it)
{
	return _node == it._node;
}
  1. operator!=
bool operator!=(const self& it)
{
	return _node != it._node;
}
  1. operator*
Ptr operator*()
{
	return _node->_data;
}
  1. operator->
Ref operator->()
{
	return &(_node->_data);
}

1.3 hash类实现普通迭代器和const迭代器

  1. 类型
typedef _iterator<K,T, T&, T*, KeyofT, Com> iterator;
typedef _iterator<K,T, const T&, const T*,KeyofT, Com> const_iterator;
  1. begin end
    begin — — 返回第一个有元素的桶的地址
    end — — 返回空指针
iterator begin()
{
	for (size_t i = 0; i < _table.size(); i++)
	{
		if (_table[i])
		{
			return iterator(_table[i], this);
		}
	}
}

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

const_iterator begin()const
{
	for (size_t i = 0; i < _table.size(); i++)
	{
		if (_table[i])
		{
			return const_iterator(_table[i], this);
		}
	}
}

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

2. unordered_set的底层逻辑

  1. SetofT — — 提取数据中的的key
struct SetofT
{
	K operator()(const K& key)
	{
		return key;
	}
};
  1. 迭代器类型
typedef typename hash_bucket::hash<K, K, SetofT>::const_iterator iterator;
typedef typename hash_bucket::hash<K, K, SetofT>::const_iterator const_iterator;
  1. begin, end
iterator begin()const
{
	return _ht.begin();
}

iterator end()const
{
	return _ht.end();
}
  1. erase
bool erase(const K& key)
{
	return _ht.erase(key);
}
  1. find
iterator find(const K& key)
{
	typename hash_bucket::hash<K, K, SetofT>::iterator ret = _ht.find(key);
	iterator res = ret;
	return res;
}
  1. insert
pair<iterator, bool>& insert(const K& key)
{
	pair<hash_bucket::hash<K, K, SetofT>::iterator, bool> ret = _ht.insert(key);
	pair<iterator, bool> res = ret;
	return res;
}

3. unordered_map的底层逻辑

  1. MapofT
struct MapofT
{
	K operator()(const pair<K, V>& kv)
	{
		return kv.first;
	}
};
  1. 迭代器类型
typedef typename hash_bucket::hash<K, pair<const K, V>, MapofT>::iterator iterator;
typedef typename hash_bucket::hash<K, pair<const K, V>, MapofT>::const_iterator const_iterator;
  1. begin, end
iterator begin()
{
	return _ht.begin();
}

iterator end()
{
	return _ht.end();
}

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

const_iterator end()const
{
	return _ht.end();
}
  1. erase
bool erase(const K& key)
{
	return _ht.erase(key);
}
  1. find
iterator find(const K& key)
{
	return _ht.find(key);
}
  1. insert
pair<iterator, bool> insert(const pair<const K,  V>& kv)
{
	return _ht.insert(kv);
}
  1. operator[ ]
V& operator[](const K& key)
{
	pair<iterator, bool> res = _ht.insert(make_pair(key, V()));
	return res.first->second;
}

4. 源码

4.1 hash类

#pragma once

#include<iostream>
#include<vector>

using namespace std;

namespace hash_bucket
{
	template<class T>
	struct HashData
	{
	public:
		HashData(const T& kv)
			:_data(kv)
		{}
	public:
		T _data;
		HashData<T>* _next;
	};

	template<class K>
	struct DEFAULT
	{
		size_t operator()(const K& key)
		{
			return (size_t)key;
		}
	};

	template<>
	struct DEFAULT<string>
	{
		size_t operator()(const string& key)
		{
			int res = 0;
			for (auto e : key)
			{
				res += e * 131;
			}

			return res;
		}
	};

	// 前置声明
	template<class K, class T, class KeyofT, class Com>
	struct hash;

	template<class K, class T, class Ptr, class Ref, class KeyofT, class Com>
	struct _iterator
	{
		typedef HashData<T> Node;
		typedef _iterator<K, T,Ptr, Ref, KeyofT, Com> self;
		typedef _iterator<K, T,T&, T*, KeyofT, Com> Iterator;

		Node* _node;
		const hash<K, T, KeyofT, Com>* _pht;


		_iterator(Node* node,const hash<K, T, KeyofT, Com>* pht)
			:_node(node)
			, _pht(pht)
		{}

		_iterator(const Iterator& node)
			:_node(node._node)
			, _pht(node._pht)
		{}

		self& operator++()
		{
			Com com;
			KeyofT kot;
			size_t hashi = com(kot(_node->_data)) % _pht->_table.size();

			if (_node->_next)
			{
				_node = _node->_next;
			}
			else
			{
				hashi++;

				while (hashi < _pht->_table.size())
				{
					if (_pht->_table[hashi])
					{
						_node = _pht->_table[hashi];
						return *this;
					}
					else
					{
						hashi++;
					}
				}

				_node = nullptr;

			}

			return *this;
		}

		bool operator!=(const self& it)
		{
			return _node != it._node;
		}

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

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

	};

	template<class K, class T, class KeyofT, class Com = DEFAULT<K>>
	struct hash
	{
		typedef HashData<T> Node;

		// 友元
		//template<class K, class Ptr, class Ref, class T, class KeyofT, class Com>
		//friend struct _iterator;

	public:
		typedef _iterator<K,T, T&, T*, KeyofT, Com> iterator;
		typedef _iterator<K,T, const T&, const T*,KeyofT, Com> const_iterator;

	public:

		iterator begin()
		{
			for (size_t i = 0; i < _table.size(); i++)
			{
				if (_table[i])
				{
					return iterator(_table[i], this);
				}
			}
		}

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

		const_iterator begin()const
		{
			for (size_t i = 0; i < _table.size(); i++)
			{
				if (_table[i])
				{
					return const_iterator(_table[i], this);
				}
			}
		}

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

		hash()
		{
			_table.resize(4, nullptr);
		}

		iterator find(const K& key)
		{
			size_t hashi = com(key) % _table.size();
			Node* cur = _table[hashi];
			while (cur)
			{
				if (com(kot(cur->_data)) == com(key))
				{
					return iterator(cur, this);
				}

				cur = cur->_next;
			}

			return iterator(nullptr, this);
		}

		pair<iterator, bool> insert(const T& kv)
		{
			iterator res = find(kot(kv));
			if (res != end())
			{
				// 不要返回空, 要返回重复值的指针, 以便与后面的 [] 来进行修改value
				return make_pair(res, false);
			}

			// 扩容逻辑
			if (_sz == _table.size())
			{
				vector<Node*> new_table;
				new_table.resize(_table.size() * 2, nullptr);
				for (size_t i = 0; i < _table.size(); i++)
				{
					Node* cur = _table[i];

					while (cur)
					{
						Node* next = cur->_next;

						size_t hashi = com(kot(cur->_data)) % new_table.size();
						cur->_next = new_table[hashi];
						new_table[hashi] = cur;

						cur = next;
					}
				}

				_table.swap(new_table);
			}

			// 插入逻辑
			size_t hashi = com(kot(kv)) % _table.size();
			Node* newnode = new Node(kv);

			newnode->_next = _table[hashi];
			_table[hashi] = newnode;

			++_sz;
			return make_pair(iterator(_table[hashi], this), true);
		}

		bool erase(const K& key)
		{
			Node* res = find(key);
			if (res == nullptr)
			{
				return false;
			}
			else
			{
				size_t hashi = com(key) % _table.size();
				Node* cur = _table[hashi];
				Node* prev = nullptr;
				while (cur)
				{
					if (cur->_data.first == key)
					{
						if (prev == nullptr)
						{
							_table[hashi] = cur->_next;
						}
						else
						{
							prev->_next = cur->_next;
						}

					}

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

				--_sz;
				delete cur;
			}

			return true;
		}

		void print()
		{
			for (int i = 0; i < _table.size(); i++)
			{
				Node* cur = _table[i];
				printf("[%d]->", i);
				while (cur)
				{
					printf("%d", cur->_data.first);
					cur = cur->_next;
				}
				cout << "NULL" << endl;
			}
			cout << endl;
		}

	public:
		vector<Node*> _table;
		size_t _sz = 0;
		Com com;
		KeyofT kot;
	};
}

4.2 unordered_set类

#pragma once

#include"hash_bucket.h"
#include<iostream>

using namespace std;

namespace muyu
{
	template<class K>
	class unordered_set
	{
		struct SetofT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};

	public:
		typedef typename hash_bucket::hash<K, K, SetofT>::const_iterator iterator;
		typedef typename hash_bucket::hash<K, K, SetofT>::const_iterator const_iterator;

		iterator begin()const
		{
			return _ht.begin();
		}

		iterator end()const
		{
			return _ht.end();
		}

		pair<iterator, bool>& insert(const K& key)
		{
			pair<hash_bucket::hash<K, K, SetofT>::iterator, bool> ret = _ht.insert(key);
			pair<iterator, bool> res = ret;
			return res;
		}

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

		iterator find(const K& key)
		{
			typename hash_bucket::hash<K, K, SetofT>::iterator ret = _ht.find(key);
			iterator res = ret;
			return res;
		}

	private:
		hash_bucket::hash<K, K, SetofT> _ht;
	};
}

4.3 unordered_map类

#pragma once

#include"hash_bucket.h"
#include<iostream>

using namespace std;

namespace muyu
{
	template<class K, class V>
	class unordered_map
	{
		struct MapofT
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};

	public:
		typedef typename hash_bucket::hash<K, pair<const K, V>, MapofT>::iterator iterator;
		typedef typename hash_bucket::hash<K, pair<const K, V>, MapofT>::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>& kv)
		{
			return _ht.insert(kv);
		}

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

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

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

	private:
		hash_bucket::hash<K, pair<const K, V>, MapofT> _ht;
	};
}

爱此倚栏干,谁同寓目闲。
轻阴弄晴日,秀色隐空山。
岛树萧疏外,征帆杳霭间。
予虽江上老,心羡白云还。
— — 岳飞 <题池州翠光寺>

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

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

相关文章

buildadmin+tp8表格操作(4) Table组件,baTable类和 elementplus中的属性关系

在buildadmin 中&#xff0c;table组件是封装的 element-plus中的方法&#xff0c; 所以说&#xff0c; 在 buildadmin的table组件中&#xff0c;是可以通用 elementplus中的属性的 以上这些属性&#xff0c; 在buildadmin中都是可以使用的 使用方式和 elementplus el-table用…

终端仿真软件 SecureCRT v9.4.2

SecureCRT是一款终端仿真软件&#xff0c;它提供了类似于Telnet和SSH等协议的远程访问功能。SecureCRT专门为网络管理员、系统管理员和其他需要保密访问网络设备的用户设计。 SecureCRT具有以下特点&#xff1a; 安全性&#xff1a;SecureCRT支持SSH1、SSH2、SSL和TLS等加密和…

git可以pull到代码,push时提示没有权限。解决办法:git生成ssh密钥(详细步骤)

1、首先右键点击电脑桌面&#xff0c;点击“git bash here”&#xff0c;打开git命令窗口 2、如果git用户名和邮箱等已经完成配置&#xff0c;则跳过此步骤&#xff0c;直接操作第3条&#xff1b;假如没有配置&#xff0c;继续如下操作&#xff1a; (1)、在命令窗口配置用户&a…

怎么实现在微信公众号预约挂号功能呢

随着移动互联网的普及&#xff0c;微信公众号已经成为人们获取信息、了解资讯、预约服务的重要渠道。为了方便患者&#xff0c;许多医院也纷纷开通了微信公众号预约挂号功能。本文将介绍如何在微信公众号上实现预约挂号功能&#xff0c;帮助患者更加便捷地预约挂号。 一、关注医…

HarmonyOS ArkTS List组件和Grid组件的使用(五)

简介 ArkUI提供了List组件和Grid组件&#xff0c;开发者使用List和Grid组件能够很轻松的完成一些列表页面。常见的列表有线性列表&#xff08;List列表&#xff09;和网格布局&#xff08;Grid列表&#xff09;&#xff1a; List组件的使用 List是很常用的滚动类容器组件&…

python接口自动化测试之接口数据依赖

一般在做自动化测试时&#xff0c;经常会对一整套业务流程进行一组接口上的测试&#xff0c;这时候接口之间经常会有数据依赖&#xff0c;那又该如何继续呢&#xff1f; 那么有如下思路&#xff1a; 抽取之前接口的返回值存储到全局变量字典中。初始化接口请求时&#xff0c;…

数据资产入表实务操作的难点及解决方案

在当前数字化的浪潮下&#xff0c;数据已经成为劳动、土地、知识、技术以后的第五大生产要素&#xff0c;“数据就是资源”已成为共识。8月21日&#xff0c;财政部正式印发《企业数据资源相关会计处理暂行规定》&#xff0c;对于符合规定定义和确认条件的数据资产&#xff0c;可…

vue3.0 + qiankun遇到的问题

进入子应用再回到主应用切换动态路由时 TypeError: Cannot read properties of undefined (reading ‘appWrapperGetter’) application ‘plat’ died in status UNMOUNTING: instance.$destroy is not a function 第一个报错是因为子应用切走时没有销毁 vue的实例&#xff0…

麒麟系统开机忘记密码,密码重置

开机后&#xff0c;麒麟系统忘密码了... ... 1.关机状态下&#xff0c;点击电源开机&#xff0c;进入GRUB界面&#xff08;这个得看自己的系统进入GRUB的方式&#xff0c;有的直接进GRUB&#xff0c;有的直接进系统启动&#xff0c;我是按的F2进入GRUB界面&#xff09;&#xf…

深度学习之自监督模型汇总

1.BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding paper:https://arxiv.org/pdf/1810.04805v2.pdf code:GitHub - google-research/bert: TensorFlow code and pre-trained models for BERT Abstract&#xff1a;我们引入了一种名为 BE…

ECharts零基础使用思路 图表案例网站推荐

1、用npm安装echarts npm i echarts -S 2、引入 &#xff08;1&#xff09;可以在mian.js里全局引入 import echarts from ‘echarts’ Vue.prototype.$echarts echarts 将echarts挂载在Vue原型上 用时直接this.$echarts即可 &#xff08;2&#xff09;也可以在组件中按需引入…

OpenAI宫斗,尘埃落定,微软成最大赢家

周末被OpenAI董事会闹剧刷屏,ChatGPT之父Sam Altman前一天被踢出董事会,免职CEO,后一天重返OpenAI,目前结局未知。 很多同学想要围观,缺少背景知识,这里老章为大家简单介绍前因后果及涉及的人物,时间线,让大家轻松围观。 备好瓜子,开始。 1、主角 先看一张图,看一…

SWAT-MODFLOW耦合

耦合模型被应用到很多科学和工程领域来改善模型的性能、效率和结果&#xff0c;SWAT作为一个地表水模型可以较好的模拟主要的水文过程&#xff0c;包括地表径流、降水、蒸发、风速、温度、渗流、侧向径流等&#xff0c;但是对于地下水部分的模拟相对粗糙&#xff0c;考虑到SWAT…

解决收集问卷难的方法与策略:提升数据收集效率

随着社会的发展和科技的进步&#xff0c;问卷调查成为了获取信息和研究数据的重要手段之一。然而&#xff0c;面临的一个普遍难题是如何解决收集问卷困难的问题。无论是在学术研究、市场调研还是社会调查中&#xff0c;都存在着一些挑战和阻碍因素。本文将从不同角度探讨如何突…

手机 IOS 软件 IPA 签名下载安装详情图文教程

由于某些应用由于某些原因无法上架到 appStore 或者经过修改过的软件游戏等无法通过 appStore 安装&#xff0c;我们就可以使用签名的方式对相应软件的IPA文件进行签名然后安装到你的手机上 这里我们使用爱思助手进行签名安装&#xff0c;爱思助手支持两种方式&#xff0c;一种…

seleninum 基础及简单实践

网页自动化 1 Selenium自动化基础 1.1 Selenium简介 Selenium自动化流程如下&#xff1a; 自动化程序调用Selenium客户端库函数客户端库会发送Selenium命令&#xff0c;给浏览器的驱动程序浏览器驱动程序接收到命令后&#xff0c;驱动浏览器去执行命令浏览器执行命令浏览器驱…

基于单片机电梯液晶显示防超重,防气体报警、防夹报警控制系统及源程序

一、系统方案 1、本设计采用51单片机作为主控器。 2、液晶显示楼层。 3、防超重&#xff0c;防气体报警、防夹报警。 二、硬件设计 原理图如下&#xff1a; 三、单片机软件设计 1、首先是系统初始化 /lcd1602初始化设置*/ void init_1602() //lcd1602初始化设置 { write_co…

财报解读:第三季度营收净利双增,中通快递的进击根本停不下来?

快递业又变天了。 在极兔上市之前&#xff0c;快递行业的格局几乎已经稳定&#xff0c;“通达系们”占据了过半的市场份额。数据显示&#xff0c;2022年按包裹量计&#xff0c;中通、圆通、申通、韵达市占率分别为22.1%、15.81%、11.71%、15.92%&#xff0c;共计占比达65.54%。…

利用互斥锁解决缓存击穿问题

核心思路&#xff1a;相较于原来从缓存中查询不到数据后直接查询数据库而言&#xff0c;现在的方案是 进行查询之后&#xff0c;如果从缓存没有查询到数据&#xff0c;则进行互斥锁的获取&#xff0c;获取互斥锁后&#xff0c;判断是否获得到了锁&#xff0c;如果没有获得到&am…

配置中心

服务配置中心介绍 首先我们来看一下,微服务架构下关于配置文件的一些问题&#xff1a; 1. 配置文件相对分散。 在一个微服务架构下&#xff0c;配置文件会随着微服务的增多变的越来越多&#xff0c;而且分散 在各个微服务中&#xff0c;不好统一配置和管理。 2. 配置文件无…