C++语法(23)-- 模拟实现unordered_set和unordered_map

news2024/11/26 18:37:20


C++语法(22)---- 哈希表的闭散列和开散列_哈里沃克的博客-CSDN博客https://blog.csdn.net/m0_63488627/article/details/130436178?spm=1001.2014.3001.5501

1.重写HashTable

由于此时我们的实现与map跟set差不多,所以需要进行调整

1.重写节点

节点通过unordered_set和unordered_map传入的东西来构造是Value类型还是pair<K,V>类型

template<class T>
struct HashNode
{
	T _data;
	HashNode<T>* _next;
	HashNode(const T& data)
	:_data(data), _next(nullptr)
	{}
};

2.重写哈希表

1.首先,一定要通过Key值查找对应的值,那么我们需要设置一个模板为K

2.我们还要传入哈希元素节点,设置模板为T(与上面一致)

3.将T的值进行哈希映射的Hash仿函数,该函数应该在unordered层传入,因为我们也不知道要映射到位置上的是什么

4.还有一个是与map和set实现一样的仿函数,用于区分到底是unordered_map还是unordered_set中的Key值的比较,该模板命名为KeyOfT

template<class K, class T, class Hash, class KeyOfT>
class HashTable
{
	typedef HashNode<T> Node;
public:
	HashTable()
		:_n(0)
	{
		_table.resize(10);
	}
	~HashTable()
	{
		//省略
	}

	bool Insert(const T& data)
	{
        //需要注意获取哈希映射的方式
        //需要用到hash转换k为对应的int再进行操作
		KeyOfT kot;
		if (Find(kot(data)) != nullptr)
			return false;

		if (_n == _table.size())
		{
			//省略怎么扩容的
		}

        //需要注意获取哈希映射的方式
        //需要用到上层传来的KeyOfT使得data变成可以被哈希映射的值
		size_t hashi = kot(data) % _table.size();

		//省略怎么插入的
	}

	Node* Find(const K& k)
	{
        //需要注意获取哈希映射的方式
        //需要用到hash转换k为对应的int再进行操作
		size_t hashi = Hash()(k) % _table.size(); 

        //省略怎么找的
	}

	bool Erase(const K& k)
	{
        //需要注意获取哈希映射的方式
        //需要用到hash转换k为对应的int再进行操作
		size_t hashi = Hash()(k) % _table.size(); 
	
        //省略怎么删除的
	}

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

2.unordered初步实现

1.unordered_set

对于set而言,传入的直接就是Key,那么SetOfT仿函数返回的就是key

namespace MY
{
	template<class K,class Hash = HashFunc<K>>
	class unordered_set
	{
		struct SetOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
	private:
		buckethash::HashTable<K, K, Hash, SetOfT> _ht;
	};
}

2.unordered_map

对于map而言,传入的是pair,那么MapOfT仿函数返回的就是pair的first

namespace MY
{
	template<class K, class V, class Hash = HashFunc<K>>
	class unordered_map
	{
		struct MapOfT
		{
			const K& operator()(const pair<const K, V>& key)
			{
				return key.first;
			}
		};
	private:
		buckethash::HashTable<K, pair<const K, V>, Hash, MapOfT> _ht;
	};
}

3. 哈希表的迭代器

template<class K, class T, class Hash, class KeyOfT>
struct __HTIterator
{
	typedef HashNode<T> Node;
	typedef __HTIterator<K, T, Hash, KeyOfT> Self;
	typedef HashTable<K, T, Hash, KeyOfT> HT;
	Node* _node;
	HT* _ht;
};

我们需要算到开始的位置,所以一定要传入哈希表的地址,随后访问到哈希表的tables。

迭代器的简单函数

__HTIterator(Node* node,HT* ht)
	:_node(node)
	,_ht(ht)
{}

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

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

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

重写++的实现

首先我们找到下一个位置有两种情况

1.指针往下就是下一个数

2.整个桶走完了,我们要找下一个桶有数据的位置

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

因为迭代器用到了哈希表,但是我将迭代器写在了哈希表的前面,所以在此之前还得先定义哈希表的前置声明

//前置声明
template<class K, class T, class Hash, class KeyOfT>
class HashTable;

 4.哈希表的迭代器形式

1.迭代器怎么才使用_table

由于哈希表中的table是私有对象,而迭代器需要访问table,那么就要将迭代器设置为哈希表的友元。

template<class K, class T, class Hash, class KeyOfT>
class HashTable
{
	typedef HashNode<T> Node;

	template<class K, class T, class Hash, class KeyOfT>
	friend struct __HTIterator; //友元

public:
	typedef __HTIterator<K, T, Hash, KeyOfT> iterator;
private:
	vector<Node*> _table;
	size_t _n = 0;
};

2.哈希表中的迭代器

需要注意的是,在迭代器中,构造函数为:

__HTIterator(Node* node,HT* ht)
	:_node(node)
	,_ht(ht)
{}

所以哈希表的函数返回包括迭代器内容的,一律需要构造迭代器返回。

_node:就是生成的节点的地址,该地址就是节点的指针,这个好获得

_ht:为哈希表的地址,那么我们在哈希表的类中,本身的对象就是_ht,我们只需要返回this

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

iterator end()
{
	return iterator(nullptr, this);
}
iterator Find(const K& k)
{
	size_t hashi = Hash()(k) % _table.size();
	Node* cur = _table[hashi];
	KeyOfT kot;
	while (cur)
	{
		if (kot(cur->_data) == k)
			return iterator(cur, this);
		else
			cur = cur->_next;
	}
	return iterator(nullptr,this);
}

bool Erase(const K& k)
{
	size_t hashi = Hash()(k) % _table.size();
	Node* cur = _table[hashi];
	Node* prev = nullptr;
	while (cur)
	{
		if (cur->_kv.first == k)
		{
			if (cur == _table[hashi])
				_table[hashi] = cur->_next;
			else
				prev->_next = cur->_next;
			delete cur;
			_n--;
			return true;
		}
		prev = cur;
		cur = cur->_next;
	}
	return false;
}

因为需要实现map的operator[],因此insert的重写是返回一个pair类型,first是迭代器,second是bool。

pair<iterator, bool> Insert(const T& data)
{
	KeyOfT kot;
			
	iterator it = Find(kot(data));
	if (it != end())
		return make_pair(it, false);

	if (_n == _table.size())
	{
		vector<Node*> newTable;
		newTable.resize(2 * _table.size(),nullptr);
		for (int i = 0; i < _table.size(); i++)
		{
			Node* cur = _table[i];
			while (cur)
			{
				Node* next = cur->_next;
				size_t hashi = Hash()(kot(cur->_data)) % newTable.size();
				cur->_next = newTable[hashi];
				newTable[hashi] = cur;
				cur = next;
			}
			_table[i] = nullptr;
		}
				_table.swap(newTable);
	}

	size_t hashi = Hash()(kot(data)) % _table.size();
	Node* newnode = new Node(data);
	newnode->_next = _table[hashi];
	_table[hashi] = newnode;
	_n++;

	return make_pair(iterator(newnode, this), true);
}

5.unordered_map和unordered_set

	template<class K, class V, class Hash = HashFunc<K>>
	class unordered_map
	{
		struct MapOfT
		{
			const K& operator()(const pair<const K, V>& key)
			{
				return key.first;
			}
		};
	public:
		typedef typename buckethash::HashTable<K, pair<const K, V>, Hash, MapOfT>::iterator iterator;

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

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

		iterator Find(const K& k)
		{
			return _ht.Find(k);
		}

		bool Erase(const K& k)
		{
			return _ht.Erase(k);
		}

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

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

	private:
		buckethash::HashTable<K, pair<const K, V>, Hash, MapOfT> _ht;
	};
	template<class K,class Hash = HashFunc<K>>
	class unordered_set
	{
		struct SetOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
	public:
		typedef typename buckethash::HashTable<K, K, Hash, SetOfT>::iterator iterator;

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

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

		iterator Find(const K& k)
		{
			return _ht.Find(k);
		}

		bool Erase(const K& k)
		{
			return _ht.Erase(k);
		}

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

	private:
		buckethash::HashTable<K, K, Hash, SetOfT> _ht;
	};

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

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

相关文章

html5前端学习

HTML5基本骨架 html标签 定义HTML文档&#xff0c;浏览器看到后就明白这个是HTML文档&#xff0c;所以其他元素要包裹在它里面&#xff0c;标签限定了文档的开始点和结束点。 <!DOCTYPE html> <html> </html> head标签 head标签用于定义文档的头部&…

实验篇(7.2) 05. 通过浏览器访问远端内网服务器 (SSL) ❀ 远程访问

【简介】直接将内网服务器映射成公网IP&#xff0c;可以方便的从任何地方访问服务器的指定端口&#xff0c;但是这种方式下&#xff0c;服务器是公开且暴露的。那有没有即方便、又比较安全的远程访问服务器的方法呢&#xff1f;我们来看看SSL VPN的Web模式。 SSL VPN介绍 从概念…

波司登云原生微服务治理探索

作者&#xff1a;曾孟琪&#xff08;山猎&#xff09; 背景 波司登创始于1976年&#xff0c;专注于羽绒服的研发、设计、制作&#xff0c;是全球知名的羽绒服生产商。波司登用一系列世人瞩目的辉煌成绩证明了自己的实力&#xff1a;连续26年全国销量领先&#xff0c;连续22年…

4种事务隔离级别 3种异常现象 死锁

4种事务隔离级别 & 3种异常现象 4种事务隔离级别和3种异常现象 事务隔离级别是指多个并发事务之间相互隔离的程度&#xff0c;用于控制事务对数据库的读取和写入操作的可见性和影响范围。在关系数据库管理系统&#xff08;RDBMS&#xff09;中&#xff0c;常见的事务隔离…

70. 爬楼梯解题思路

文章目录 题目解题思路 题目 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢&#xff1f; 示例 1&#xff1a; 输入&#xff1a;n 2 输出&#xff1a;2 解释&#xff1a;有两种方法可以爬到楼顶。 1 阶 …

【原创】用 VisualGLM 进行AIGC多模识别和内容生成

最近几个月&#xff0c;整个AI行业的LLM&#xff08;大语言模型&#xff09;蓬勃发展&#xff0c;除了过去传统的纯文字的多模态能力的视觉语言模型&#xff0c;如 GPT-4&#xff0c;ImageBind等表现令人印象深刻。 ChatGLM-6B是中文用户使用非常舒服的一个开源中文LLM。2023年…

Nginx的Rewrite的运用

Rewrite 一、常用的Nginx正则表达式二、lication三、rewrite$request_uri&#xff1a;包含请求参数的原始URI&#xff0c;不包含主机名&#xff0c;如&#xff1a;http://www.kgc.com/abc/bbs/index.html?a1&b2 中的 /abc/bbs/index.php?a1&b2 $uri&#xff1a;这个变…

chatgpt赋能python:Python去除重复字符并排序

Python去除重复字符并排序 在日常编程工作中&#xff0c;我们常常需要对列表或字符串中的重复字符进行去除&#xff0c;并将剩余字符进行排序。Python是一门非常适合进行此类操作的语言&#xff0c;其内置的数据结构和函数可以让我们轻松应对这一需求。 介绍 当我们需要对一…

【六一】【海思SS528】GPIO寄存器操作 - 使能GPIO管脚输出高、低电平

目录 一、概述二、配置复用控制寄存器&#xff0c;使能GPIO功能三、配置GPIO_DIR寄存器&#xff0c;选择输出四、配置GPIO_DATA寄存器&#xff0c;输出高电平五、测试 一、概述 这篇文章根据海思SS528芯片提供的《22AP30 H.265编解码处理器用户指南.pdf》文档(文档路径&#xf…

MIT6.042学习笔记(一)——强归纳法,数论(1)

如果学生在学校里学习的结果是使自己什么也不会创造&#xff0c;那他的一生永远是模仿和抄袭。——列夫托尔斯泰 文章目录 引言强归纳法例题&#xff1a;拆积木游戏证明 数论&#xff08;第一节&#xff09;整除运算例题&#xff1a;取水证明 公约数例题证明 Eucild算法 引言 …

C语言-static的用法

1、static 关键字 C 语言中 static 关键字修饰变量和函数。 static有三种不同的用法&#xff1a; 1.修饰局部变量&#xff1b; 2.修饰全局变量&#xff1b; 3.修饰函数 局部变量:当函数第一被调用&#xff0c;函数中的静态局部变量被初始化&#xff0c;再次调用这个函数&…

gcc/g++

文章目录 sudo 提权添加白名单gcc / g预处理编译汇编链接 sudo 提权添加白名单 1.寻找root用户 在 /etc/sudoers 文件中修改 添加普通用户的白名单 :/root找到root的白名单所在行数 2.wq!强制保存退出&#xff0c;即可添加成功 gcc / g 推荐写法 gcc mycode.c -o mytest预…

Python绘制柱状图堆叠图

本文详细介绍如何使用 Matplotlib 绘制柱状堆叠图 文章目录 一、引入库二、数据准备三、绘制基本柱状堆叠图1.绘制基本图形2.设置柱子宽度、添加刻度标签和旋转角度 四、完整代码五、运行结果六、python绘图往期系列文章目录 一、引入库 import matplotlib.pyplot as plt imp…

Hyperledger Fabric 超级账本学习【14】Fabcar实例——通过 Nodejs命令 调用链码

文章目录 Hyperledger Fabric2.X 网络 以后对应的 Node Npm 版本需要升级&#xff0c;默认版本太低&#xff0c;后面会报错启动 Fabcar 网络报错运行以下命令来杀死当前运行或者活跃的容器&#xff1a;清除所有缓存网络&#xff1a;添加映射文件进入javascript目录在此文件夹内…

初阶数据结构(7)(树形结构的概念和相关重要定义、树的表示形式、树的应用、二叉树【两种特殊的二叉树、性质、存储、遍历、基本操作、二叉树相关的 OJ 题】)

接上次博客&#xff1a;初阶数据结构&#xff08;6&#xff09;&#xff08;队列的概念、常用的队列方法、队列模拟实现【用双向链表实现、用数组实现】、双端队列 (Deque)、OJ练习【用队列实现栈、用栈实现队列】&#xff09;_di-Dora的博客-CSDN博客 目录 树形结构的概念 …

DPDK官方文档说明

目录 1、Release Notes 2、Getting Started Guide for Linux/FreeBSD/Windows 3、Programmer’s Guide 4、API Reference 5、Sample Applications User Guide 6、DPDK Tools User Guides 7、Testpmd Application User Guide 8、Network Interface Controller Drivers …

网站优化,如何挖掘长尾关键词?

做网站优化来说&#xff0c;挖掘一些长尾关键词的重要性是非常大的&#xff0c;因为长尾关键词给我们带来的流量可能会超过我们的主关键词。如何挖掘长尾关键词&#xff0c;挖掘长尾关键词的的方式有哪些是一个重要的问题。 长尾词挖掘方式&#xff1a; 【1】使用长尾词挖掘工…

机器学习——线性回归、梯度下降

文章目录 一、机器学习的分类二、线型回归Linear regression&#xff08;单变量线性回归&#xff09;三、代价函数3.1 建模误差3.2 平方误差代价函数 Squared error cost function3.3 梯度下降3.4 梯度下降与线性回归相结合 一、机器学习的分类 监督学习&#xff1a;学习数据带…

生成模型之高斯判别分析(GDA)和贝叶斯

生成模型与判别模型的区别 判别模型的学习算法学习给定x下的条件分布p(y|x; θ)&#xff0c; 例如&#xff0c;Logistic Regression&#xff08;对数几率回归&#xff09;将p(y|x; θ)建模为&#xff0c;g是sigmoid函数。 考虑一个分类问题&#xff0c;基于动物的某些特征想…

电商数据监测:如何获取想要的电商平台数据?

随着电商行业的发展&#xff0c;越来越多的企业开始通过电商平台销售商品。为了更好地掌握市场信息和消费者需求&#xff0c;企业需要获取电商平台上的数据。这些数据可以帮助企业制定营销策略、优化产品设计和提高竞争力。本文将介绍如何使用电商API获取想要的电商平台数据。 …