unordered系列容器的实现

news2024/11/15 7:53:46

1. unordered_set与unordered_map的结构

我们知道STL中的unordered_setunordered_map底层就是一个开散列的哈希表

1.1 unordered_set的结构

我们知道unordered_set其实就是K模型,所以unordered_set容器对红黑树的封装如下:

	template<class k, class Hash = Hashfunc<k>>
	class unordered_set
	{
		struct SetKeyofT
		{
			const k& operator()(const k& key)
			{
				return key;
			}
		};

	public:

    private:
		HashTable<k, const k, SetKeyofT, Hash> _t;
	};

由于unordered_set 和 unordered_map 底层都是哈希表,所以我们需要传一个仿函数来方便后期比较key值

1.2 unordered_map的结构

我们也知道unordered_map其实就是KV模型,所以unordered_map容器对红黑树的封装如下:

	template<class k, class T, class Hash = Hashfunc<k>>
	class unordered_map
	{
		struct MapKeyofT
		{
			const k& operator()(const pair<k, T>& key)
			{
				return key.first;
			}
		};
	public:

	private:
		HashTable<k, pair<const k, T>, MapKeyofT, Hash> _t;
	};

其中为了防止K值被修改,我们都加上const修饰。

2. 改造哈希表

2.1. 模板参数列表的改造

  • K:关键码类型
  • T: 不同容器T的类型不同,如果是unordered_map,T代表一个键值对,如果是unordered_set,T为 K
  • KeyofT: 因为T的类型不同,通过value取key的方式就不同,详细见unordered_map/set的实现
  • Hash: 哈希函数仿函数对象类型,哈希函数使用除留余数法,需要将Key转换为整形数字才能取模
    //哈希表的节点
	template<class T>
	struct HashNode
	{
		T _kv;
		HashNode<T>* _next;

		HashNode(const T& kv)
			: _kv(kv)
			,_next(nullptr)
		{ }
	};

    template<class k, class T, class KeyofT, class Hash>
    class HashTable

2.2. 增加迭代器操作

注意:因为哈希桶在底层是单链表结构,所以哈希桶的迭代器不需要--操作

	//为了实现简单,在哈希桶的迭代器类中需要用到 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>
	class IteratorHash
	{
		typedef HashNode<T> Node;
		typedef IteratorHash<k, T, ref, ptr, KeyofT, Hash> self;

		Node* _node;
		const HashTable<k, T, KeyofT, Hash>* _tbptr;
	public:
		IteratorHash(Node* node, const HashTable<k, T, KeyofT, Hash>* tbptr)
			:_node(node)
			,_tbptr(tbptr)
		{ }


		ref operator*()
		{
			return _node->_kv;
		}

		ptr operator->()
		{
			return &_node->_kv;
		}

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

		self& operator++()
		{
			if (_node->_next != nullptr)
			{
				_node = _node->_next;
			}
			else
			{
				KeyofT kot;
				Hash ht;
				size_t hashi = ht(kot(_node->_kv)) % _tbptr->_tables.size();
				hashi++;
				while (hashi < _tbptr->_tables.size() && _tbptr->_tables[hashi] == nullptr)
				{
					hashi++;
				}
				if (hashi == _tbptr->_tables.size())
				{
					_node = nullptr;
				}
				else
				{
					_node = _tbptr->_tables[hashi];
				}
			}
			return *this;
		}

	};

Find函数

		Iterator Find(const k& key)
		{
			Hash hot;
			KeyofT kot;
			int hashi = hot(key) % _tables.size();
			Node* root = _tables[hashi];
			while (root)
			{
				if (kot(root->_kv) == key)
				{
					return Iterator(root, this);
				}
				root = root->_next;
			}
			return Iterator(nullptr, this);
		}

在哈希表中的哈希函数一般都需要进行取模操作,但是有些自定义类型如string就无法直接进行取模操作,这时我们就需要通过某种方法将string转化为整型,然后再带入哈希函数求对应的下标。但遗憾的是,我们无法找到一种能实现字符串和整型之间一对一转换的方法,因为在计算机中,整型的大小是有限的,比如用无符号整型能存储的最大数字是4294967295,但是不同字符串能组合的数字是无限的,以无限对有限,这就意味着无论哪种哈希函数都可能存在哈希冲突。

所以我们可以针对string类型写一个特化版本。 

template<class k>
struct Hashfunc
{
	size_t operator()(const k& _kv)
	{

		return (size_t)_kv;
	}
};

template<>
struct Hashfunc<string>
{
	size_t operator()(const string& _kv)
	{
		size_t a = 0;
		for (auto e : _kv)
		{
			a += e;
			a *= 31;
		}
		return a;
	}
};

 

	template<class k, class Hash = Hashfunc<k>>
	class unordered_...
    {

   	};

Insert函数

		pair<Iterator, bool> Insert(const T& kv)
		{
			Hash hot;
			KeyofT kot;
			if (_n == _tables.size())
			{
				vector<Node*> _newtables(_tables.size() * 2, nullptr);
				for (size_t i = 0; i < _tables.size(); i++)
				{
					while(_tables[i])
					{
						Node* root = _tables[i]->_next;
						int hashi = hot(kot(_tables[i]->_kv)) % (_tables.size()*2);
						_tables[i]->_next = _newtables[hashi];
						_newtables[hashi] = _tables[i];
						_tables[i] = root;
					}
				}
				_tables.swap(_newtables);
			}
			int hashi = hot(kot(kv)) % _tables.size();
			Node* root = _tables[hashi];
			_tables[hashi] = new Node(kv);
			_tables[hashi]->_next = root;
			_n++;
			return { Iterator(_tables[hashi], this), true};
		}

Erase函数

		bool Erase(const k& key)
		{
			Hash hot;
			KeyofT kot;
			int hashi = hot(key) % _tables.size();
			Node* root = _tables[hashi];
			Node* prev = nullptr;
			while (root)
			{
				if (kot(root->_kv) == key)
				{
					if (prev == nullptr)
					{
						_tables[hashi] = root->_next;
					}
					else
					{
						prev->_next = root->_next;
					}
					delete root;
					_n--;
					return true;
				}
				prev = root;
				root = root->_next;
			}
			return false;
		}

3.整体代码如下

Hashtable:

template<class k>
struct Hashfunc
{
	size_t operator()(const k& _kv)
	{

		return (size_t)_kv;
	}
};

template<>
struct Hashfunc<string>
{
	size_t operator()(const string& _kv)
	{
		size_t a = 0;
		for (auto e : _kv)
		{
			a += e;
			a *= 31;
		}
		return a;
	}
};

namespace bit
{

	template<class T>
	struct HashNode
	{
		T _kv;
		HashNode<T>* _next;

		HashNode(const T& kv)
			: _kv(kv)
			,_next(nullptr)
		{ }
	};

	template<class k, class T, class KeyofT, class Hash>
	class HashTable;

	template<class k, class T, class ref, class ptr, class KeyofT, class Hash>
	class IteratorHash
	{
		typedef HashNode<T> Node;
		typedef IteratorHash<k, T, ref, ptr, KeyofT, Hash> self;

		Node* _node;
		const HashTable<k, T, KeyofT, Hash>* _tbptr;
	public:
		IteratorHash(Node* node, const HashTable<k, T, KeyofT, Hash>* tbptr)
			:_node(node)
			,_tbptr(tbptr)
		{ }


		ref operator*()
		{
			return _node->_kv;
		}

		ptr operator->()
		{
			return &_node->_kv;
		}

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

		self& operator++()
		{
			if (_node->_next != nullptr)
			{
				_node = _node->_next;
			}
			else
			{
				KeyofT kot;
				Hash ht;
				size_t hashi = ht(kot(_node->_kv)) % _tbptr->_tables.size();
				hashi++;
				while (hashi < _tbptr->_tables.size() && _tbptr->_tables[hashi] == nullptr)
				{
					hashi++;
				}
				if (hashi == _tbptr->_tables.size())
				{
					_node = nullptr;
				}
				else
				{
					_node = _tbptr->_tables[hashi];
				}
			}
			return *this;
		}

	};


	template<class k, class T, class KeyofT, class Hash>
	class HashTable
	{
		typedef HashNode<T> Node;
	public:
		template<class k, class T, class ref, class ptr,class KeyofT, class Hash>
		friend class IteratorHash;
		typedef IteratorHash<k, T, T&, T*, KeyofT, Hash> Iterator;
		typedef IteratorHash<k, T, const T&, const T*, KeyofT, Hash> ConstIterator;

		HashTable()
		{
			_tables.resize(10, nullptr);
		}

		~HashTable()
		{
			for (size_t i = 0; i < _tables.size(); i++)
			{
				while (_tables[i])
				{
					Node* root = _tables[i]->_next;
					delete _tables[i];
					_tables[i] = root;
				}
				_tables[i] = nullptr;
			}
		}

		Iterator Begin()
		{
			if (_n == 0) return Iterator(nullptr, this);
			else
			{
				int i = 0;
				while (_tables[i] == nullptr)
				{
					i++;
				}
				return Iterator(_tables[i], this);
			}
		}

		ConstIterator Begin()const
		{
			if (_n == 0) return ConstIterator(nullptr, this);
			else
			{
				int i = 0;
				while (_tables[i] == nullptr)
				{
					i++;
				}
				return ConstIterator(_tables[i], this);
			}
		}

		Iterator End()
		{
			return Iterator(nullptr, this);
		}		
		
		ConstIterator End()const
		{
			return ConstIterator(nullptr, this);
		}

		pair<Iterator, bool> Insert(const T& kv)
		{
			Hash hot;
			KeyofT kot;
			if (_n == _tables.size())
			{
				vector<Node*> _newtables(_tables.size() * 2, nullptr);
				for (size_t i = 0; i < _tables.size(); i++)
				{
					while(_tables[i])
					{
						Node* root = _tables[i]->_next;
						int hashi = hot(kot(_tables[i]->_kv)) % (_tables.size()*2);
						_tables[i]->_next = _newtables[hashi];
						_newtables[hashi] = _tables[i];
						_tables[i] = root;
					}
				}
				_tables.swap(_newtables);
			}
			int hashi = hot(kot(kv)) % _tables.size();
			Node* root = _tables[hashi];
			_tables[hashi] = new Node(kv);
			_tables[hashi]->_next = root;
			_n++;
			return { Iterator(_tables[hashi], this), true};
		}

		Iterator Find(const k& key)
		{
			Hash hot;
			KeyofT kot;
			int hashi = hot(key) % _tables.size();
			Node* root = _tables[hashi];
			while (root)
			{
				if (kot(root->_kv) == key)
				{
					return Iterator(root, this);
				}
				root = root->_next;
			}
			return Iterator(nullptr, this);
		}

		bool Erase(const k& key)
		{
			Hash hot;
			KeyofT kot;
			int hashi = hot(key) % _tables.size();
			Node* root = _tables[hashi];
			Node* prev = nullptr;
			while (root)
			{
				if (kot(root->_kv) == key)
				{
					if (prev == nullptr)
					{
						_tables[hashi] = root->_next;
					}
					else
					{
						prev->_next = root->_next;
					}
					delete root;
					_n--;
					return true;
				}
				prev = root;
				root = root->_next;
			}
			return false;
		}


	private:
		vector<Node*> _tables;
		size_t _n = 0;
	};
}

unordered_set:

	#include"hashtable.h"

    template<class k, class Hash = Hashfunc<k>>
	class unordered_set
	{
		struct SetKeyofT
		{
			const k& operator()(const k& key)
			{
				return key;
			}
		};

	public:
		typedef typename HashTable<k, const k, SetKeyofT, Hash>::Iterator iterator;
		typedef typename HashTable<k, const k, SetKeyofT, Hash>::ConstIterator const_iterator;

		iterator begin()
		{
			return _t.Begin();
		}		
		
		const_iterator begin()const
		{
			return _t.Begin();
		}

		iterator end()
		{
			return _t.End();
		}		
		
		const_iterator end()const
		{
			return _t.End();
		}

		pair<iterator, bool> insert(const k& key)
		{
			return _t.Insert(key);
		}

		iterator find(const k& key)
		{
			_t.Find(key);
		}

		bool erase(const k& key)
		{
			return _t.Erase(key);
		}


	private:
		HashTable<k, const k, SetKeyofT, Hash> _t;
	};

unordered_map:

#include"hashtable.h"


namespace bit
{

	template<class k, class T, class Hash = Hashfunc<k>>
	class unordered_map
	{
		struct MapKeyofT
		{
			const k& operator()(const pair<k, T>& key)
			{
				return key.first;
			}
		};
	public:
		typedef typename HashTable<k, pair<const k, T>, MapKeyofT, Hash>::Iterator iterator;
		typedef typename HashTable<k, pair<const k, T>, MapKeyofT, Hash>::ConstIterator const_iterator;


		iterator begin()
		{
			return _t.Begin();
		}

		const_iterator begin()const
		{
			return _t.Begin();
		}

		iterator end()
		{
			return _t.End();
		}

		const_iterator end()const
		{
			return _t.End();
		}

		pair<iterator, bool> insert(const pair<k, T>& kv)
		{
			return _t.Insert(kv);
		}

		T& operator[](const k& k)
		{
			return (*(insert({ k, T() }).first)).second;
		}

		iterator find(const k& k)
		{
			return _t.Find(k);
		}

		bool erase(const k& k)
		{
			return _t.Erase(k);
		}

	private:
		HashTable<k, pair<const k, T>, MapKeyofT, Hash> _t;
	};

感谢大家的观看!

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

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

相关文章

VTK随笔十二:体绘制(体绘制管线、vtkVolumeMapper、vtkVolume、不规则网格数据体绘制技术 )

体绘制&#xff0c;有时又称作三维重建(区别于投影图像的三维重建)&#xff0c;是一种直接利用体数据来生成二维图像的绘制技术。与面绘制不同&#xff0c;体绘制技术不需要提取体数据内部的等值面&#xff0c;它是一个对三维体数据进行采样和合成的过程。体绘制能够通过设置不…

【深度学习入门】计算机视觉任务

一、引言 对于神经网络&#xff0c;可以把中间的隐藏层看作一个黑盒子&#xff0c;这个黑盒子能自动选择如何提取特征&#xff0c;这不同于传统机器学习的人工操作&#xff0c;它的实现原理也是我们学习深度学习的重点。本文章以计算机视觉任务中的图像分类任务为例子&#xff…

zookeeper 集群搭建 及启动关闭脚本

1准备奇数台机子3&#xff0c;5&#xff0c;7 我准备的是三台 192.168.58.81 zookeeper-1 192.168.58.82 zookeeper-2 192.168.58.83 zookeeper-3 下载jdk 把他配置环境变量并检查是否是环境变量 echo $JAVA_HOME cd /opt/software wget http://mirrors.hust.edu.…

docker部署project-exam-system项目

8月30日笔记 项目实战&#xff1a;使用docker部署project-exam-system 1、背景&#xff1a; 使用基础的docker指令来创建镜像&#xff0c;实现项目的发布&#xff0c;使用Dockderfile&#xff0c;docker compose编排容器。 2、环境准备&#xff1a; &#xff08;1&#x…

sts 0/1 没有 pod生成 -> kube-controller-manager没了

kube-controller-manager.yaml 在 nerdctl ps -a 看不到 journalctl -xu kubelet > /tmp/kubelet.log /tmp/kubelet.log 老6

OpenSetting组件的用法

文章目录 1. 概念介绍2. 使用方法与主要功能2.1 使用方法2.2 主要功能 3. 示例代码4. 内容总结 我们在上一章回中介绍了"如何获取App自身信息"相关的内容&#xff0c;本章回中将介绍一个三方包:open_setting.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 概念…

JavaWeb:实验二JSP表单开发及访问数据库

一、实验目的 1&#xff0e;掌握JSP表单的开发方法。 2&#xff0e;熟悉JDBC技术和使用JDBC连接各种数据库。 二、实验性质 综合性实验 三、实验内容 实现注册与登录功能&#xff1a; 1.创建一个数据库&#xff0c;在数据库建立用户表。&#xff08;5分&#xff09; 2…

城市管理违规行为智能识别 Task3学习心得

本次学习主要针对数据集增强和模型预测 1、数据增强&#xff1a; 1&#xff09;将四张训练图像组合成一张&#xff0c;增加物体尺度和位置的多样性。 2&#xff09;复制一个图像的随机区域并粘贴到另一个图像上&#xff0c;生成新的训练样本 3&#xff09;图像的随机旋转、…

day25 Java基础——面向对象两万字详解!(纯干货)

day25 Java基础——面向对象两万字详解&#xff01;&#xff08;纯干货&#xff09; 文章目录 day25 Java基础——面向对象两万字详解&#xff01;&#xff08;纯干货&#xff09;1. 类与对象的关系类&#xff08;Class&#xff09;对象&#xff08;Object&#xff09;类与对象…

gitee绑定公钥后依旧无法使用_gitee push添加公钥无效

解决&#xff1a; 步骤按照官网操作即可&#xff1a;gitee官方说明 看看远程地址是否使用的http模式&#xff0c;是的话换ssh模式

Adobe Acrobat Reader的高级功能详解

​ 大家好&#xff0c;我是程序员小羊&#xff01; 前言&#xff1a; Adobe Acrobat Reader是广泛使用的PDF查看器&#xff0c;其不仅可以用于查看和打印PDF文件&#xff0c;还具有一些高级功能&#xff0c;可以提升用户的PDF文档处理效率。以下是对Adobe Acrobat Reader的一些…

RH850系列芯片深度剖析 1.7-启动流程解析

RH850系列芯片深度剖析 1.7-启动流程解析 文章目录 RH850系列芯片深度剖析 1.7-启动流程解析一、简介二、启动相关文件说明三、启动相关的段四、启动流程4.1 启动文件启动4.1.1 寄存器初始化4.1.2 时钟初始化4.1.3 模块Standby寄存器初始化4.1.4 启动从核4.1.5 硬件初始化4.1.5…

vector模拟实现迭代器失效

目录 1.vector和vector> 1.1两者的区别 1.2遍历的方法 2.vector模拟实现的准备 3.reserve出现的问题及解决方案 4.遍历vector的三种方式 5.关于typename的使用 6.insert导致的迭代其实失效问题 6.1因为扩容导致的迭代器失效 6.2因为插入数据倒置的迭代器失效 1.vec…

【精彩回顾·成都】成都 UG 生成式 AI 工作坊:AI 革命下的商业模式创新!

文章目录 前言一、活动介绍二、精彩分享内容及活动议程2.1、亚马逊云科技社区情况和活动介绍2.2、《浅谈 AIGC 商业化》2.3、《AI 浪潮下的产品落地》2.4、现场互动情况2.5、休息茶歇时间2.6、《AI 赋能商业革新&#xff1a;智能化转型的策略与实践》2.7、《动手实践&#xff1…

GAMES104:10+11游戏引擎中物理系统的基础理论算法和高级应用-学习笔记

文章目录 概览一&#xff0c;物理对象与形状1.1 对象 Actor1.2 对象形状Actor Shape 二&#xff0c;力与运动2.1 牛顿定律2.2 欧拉法2.2.1 显式欧拉法Explicit (Forward) Euler’s Method2.2.2 隐式欧拉法 Implicit (Backward) Euler’s Method2.2.3 半隐式欧拉法 Semi-implici…

【iOS】通过第三方库Masonry实现自动布局

目录 前言 约束 添加约束的规则 使用Masonry自动布局 Masonry的常见使用方法 补充 前言 在暑期完成项目时&#xff0c;经常要花很多时间在调试各种控件的位置上&#xff0c;因为每一个控件的位置都需要手动去计算&#xff0c;在遇到循环布局的控件时&#xff0c;还需要设…

使用文件系统管理硬件设备

1、描述一个文件系统 介绍如何使用文件系统来管理计算机系统中所有的硬件设计和磁盘 之前直接在sys_open/sys_read/sys_read中对具体的tty设备的打开、读写进行操作&#xff0c;并且只是非常粗浅地介绍了文件系统的一些概念。接下来将正式引入文件系统 由于文件系统需要考虑不…

从FasterTransformer源码解读开始了解大模型(2.4)代码通读05

从FasterTransformer源码解读开始了解大模型&#xff08;2.4&#xff09;代码解读05-ContextDecoder的前向01 写在前面的话 ContextDecoder部分是用于处理输入部分的组件层&#xff0c;在这一层中&#xff0c;会对所有输入的input ids进行处理&#xff0c;计算Attention&…

全国大学生数学建模比赛——关联规则

一、问题背景与关联规则适用性 在数学建模比赛中&#xff0c;常常会遇到需要分析大量数据以寻找变量之间潜在关系的问题。关联规则分析作为一种数据挖掘技术&#xff0c;特别适用于这种场景。例如&#xff0c;在一些实际问题中&#xff0c;可能需要从众多的因素中找出哪些因素之…

基于Python的量化交易回测框架Backtrader初识记录(一)

版权声明&#xff1a;本文为博主原创文章&#xff0c;如需转载请贴上原博文链接&#xff1a;基于Python的量化交易回测框架Backtrader初识记录&#xff08;一&#xff09;-CSDN博客 前言&#xff1a;近期以来&#xff0c;对股市数据获取及预处理算是告一段落&#xff0c;下一步…