unordered_map、unordered_set底层封装

文章目录

    • 一、先实现哈希桶
      • 1.1哈希桶的实现方法
      • 1.2日常普遍的哈希桶存放的数据有两种:字符串和整形
      • 1.3哈希桶的实现代码+详解
        • 1.3.1哈希桶的两种仿函数(int和string)
        • 1.3.2哈希桶的节点(如果桶非常深,这里考虑挂红黑树)
        • 1.3.3哈希表的构造函数、析构函数
        • 1.3.4哈希表的查找和删除(和析构函数的原理类似)
        • 1.3.5哈希表的插入(这里插入需要判断总共桶的深度是否大于桶的长度,大于就扩容,桶太深不易于查找)
      • 1.4哈希桶的测试
    • 二、unordered_map、unordered_set的仿函数封装
      • 2.1先封装仿函数
      • 2.2加入迭代器(迭代器里面要存这张表,还有这个这个节点,然后还有对应的桶的序号)
      • 2.3哈希桶改造(加入迭代器,为了使迭代器能够访问到表里面的私有成员,我们加了一个友元)
      • 2.4unordered_map、unordered_set的内部封装
      • 2.5const迭代器的改造
    • 三、封装代码(比库里面的快一些,因为(在VS编辑器下)库里面的unordered_map、unordered_set里面需要多维护维护一个指针,这个指针是从插入第一个节点开始维护的)

在这里插入图片描述

一、先实现哈希桶

1.1哈希桶的实现方法

在这里插入图片描述

1.2日常普遍的哈希桶存放的数据有两种:字符串和整形

为了减少字符串的误判(比如"abcd" 和 “acbd”),我们每次加上一个字符串乘以一个数(这里我们可以查看字符串哈希算法)
在这里插入图片描述
这里我们每次就乘以31。

1.3哈希桶的实现代码+详解

1.3.1哈希桶的两种仿函数(int和string)

在这里插入图片描述

1.3.2哈希桶的节点(如果桶非常深,这里考虑挂红黑树)

在这里插入图片描述

1.3.3哈希表的构造函数、析构函数

在这里插入图片描述

1.3.4哈希表的查找和删除(和析构函数的原理类似)

在这里插入图片描述
这里有一点点的细节处理:需要判断该桶删除的位置到底是头,还是其他位置,分一下类

1.3.5哈希表的插入(这里插入需要判断总共桶的深度是否大于桶的长度,大于就扩容,桶太深不易于查找)

在这里插入图片描述

1.4哈希桶的测试

在这里插入图片描述
在这里插入图片描述

二、unordered_map、unordered_set的仿函数封装

2.1先封装仿函数

在这里插入图片描述

2.2加入迭代器(迭代器里面要存这张表,还有这个这个节点,然后还有对应的桶的序号)

在这里插入图片描述

2.3哈希桶改造(加入迭代器,为了使迭代器能够访问到表里面的私有成员,我们加了一个友元)

在这里插入图片描述

2.4unordered_map、unordered_set的内部封装

在这里插入图片描述
在这里插入图片描述

2.5const迭代器的改造

在这里插入图片描述

三、封装代码(比库里面的快一些,因为(在VS编辑器下)库里面的unordered_map、unordered_set里面需要多维护维护一个指针,这个指针是从插入第一个节点开始维护的)

unordered_map.h

#pragma once
#include"HashTable.h"
namespace SF
{
	template<class K,class V,class Hash = HashFunc<K>>
	class unordered_map
	{
		struct KeyOfMap
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};
	public:
		//HashTable<K, pair<const K, V>, Hash, KeyOfMap>
		typedef typename hash_bucket::HashTable<K, pair<const K, V>, Hash, KeyOfMap>::iterator iterator;

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

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

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

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

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

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

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



	private:
		hash_bucket::HashTable<K, pair<const K,V>, Hash, KeyOfMap> _ht;
	};

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

		auto iter = dict.begin();
		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;
	}
}




unordered_set.h

#pragma once
#include"HashTable.h"
namespace SF
{
	template<class K,class Hash = HashFunc<K>>
	class unordered_set
	{
		struct KeyOfSet
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
		

	public:
		typedef typename hash_bucket::HashTable<K,K, Hash, KeyOfSet>::iterator iterator;
		typedef typename hash_bucket::HashTable<K,K, Hash, KeyOfSet>::const_iterator const_iterator;

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

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

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

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

		pair<const_iterator, bool> insert(const K& key)
		{
			auto ret = _ht.Insert(key);
			return pair<const_iterator, bool>(const_iterator(ret.first._pht,
				ret.first._node, ret.first._hashi),
				ret.second);
		}

		

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

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

	private:
		hash_bucket::HashTable<K,K, HashFunc<K>, KeyOfSet> _ht;
	};

	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;
	}
}
HashTable.h


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

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


template<>
struct HashFunc<string>//显示实例化
{
	size_t operator()(const string& kv)
	{
		size_t hashi = 0;
		for (auto e : kv)
		{
			hashi *= 31;
			hashi += e;
		}
		return hashi;
	}
};


namespace hash_bucket
{
	template<class T>
	struct HashNode
	{
		T _data;
		HashNode<T>* _next;

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

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

	template<class K,class T,class Ptr,class Ref,class KeyOfT,class Hash>
	struct _HTIterator
	{
		typedef HashNode<T> Node;
	public:
		//迭代器里面需要指向该位置的节点,然后还需要这张表,不然无法判断下一个位置,
		// 然后还需要该节点处于哪一个位置
		typedef _HTIterator<K, T, Ptr, Ref, KeyOfT, Hash> Self;
		typedef _HTIterator<K, T, T*, T&, KeyOfT, Hash> iterator;


		size_t _hashi;//该节点在哪一个桶
		Node* _node;
		const HashTable<K, T, Hash, KeyOfT>* _pht;

		_HTIterator(HashTable<K, T, Hash, KeyOfT>* pht,Node* node,size_t hashi)
			:_pht(pht)
			,_node(node)
			,_hashi(hashi)
		{}

		_HTIterator(const HashTable<K, T, Hash, KeyOfT>* pht, Node* node, size_t hashi)
			:_pht(pht)//避免权限放大的影响
			, _node(node)
			, _hashi(hashi)
		{}

		_HTIterator(const iterator& iter) 
			:_pht(iter._pht)
			, _node(iter._node)
			, _hashi(iter._hashi)
		{}
		bool operator!=(const Self& s)
		{
			return _node != s._node;
		}

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

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

		Self& operator++()
		{
		

			if (_node->_next)
			{
				// 当前桶还有节点,走到下一个节点
				_node = _node->_next;
			}
			else
			{
				// 当前桶已经走完了,找下一个桶开始
				//KeyOfT kot;
				//Hash hf;
				//size_t hashi = hf(kot(_node->_data)) % _pht._tables.size();
				++_hashi;
				while (_hashi < _pht->_tables.size())
				{
					if (_pht->_tables[_hashi])
					{
						_node = _pht->_tables[_hashi];
						break;
					}

					++_hashi;
				}

				if (_hashi == _pht->_tables.size())
				{
					_node = nullptr;
				}
			}

			return *this;
		}
	};



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

		template<class K,class T,class Ptr,class Ref,class KeyOfT,class Hash>
		friend struct _HTIterator;

		typedef _HTIterator<K, T, T*, T&, KeyOfT, Hash> iterator;
		typedef _HTIterator<K, T,const T*, const T&, KeyOfT, Hash> const_iterator;
	public:
		iterator begin()
		{
			for (size_t i = 0; i < _tables.size(); i++)
			{
				if (_tables[i])
					return iterator(this, _tables[i], i);
			}
			return end();
		}

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

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

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

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

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

		iterator Find(const K& key)
		{
			Hash hf;
			KeyOfT kot;

			size_t hashi = hf(key) % _tables.size();
			Node* cur = _tables[hashi];
			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					return iterator(this, cur, hashi);
				}

				cur = cur->_next;
			}

			return end();
		}

		bool Erase(const K& key)
		{
			Hash hf;
			KeyOfT kot;

			size_t hashi = hf(key) % _tables.size();
			Node* prev = nullptr;
			Node* cur = _tables[hashi];
			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					if (prev == nullptr)
					{
						_tables[hashi] = cur->_next;
					}
					else
					{
						prev->_next = cur->_next;
					}
					delete cur;

					return true;
				}

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

			return false;
		}

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

			if (_n == _tables.size())
			{
				HashTable<K, T, Hash,KeyOfT> newtable;
				newtable._tables.resize(2 * _tables.size(), nullptr);
				for (size_t i = 0; i < _tables.size(); i++)
				{
					Node* cur = _tables[i];
					while (cur)
					{
						size_t hashi = hf(kot(cur->_data)) % newtable._tables.size();
						newtable._tables[hashi] = cur;
						cur = cur->_next;
					}
					_tables[i] = nullptr;
				}
				_tables.swap(newtable._tables);//转移资源
			}
			_n++;
			size_t hashi = hf(kot(data)) % _tables.size();
			Node* newnode = new Node(data);
			//头插
			newnode->_next = _tables[hashi];
			_tables[hashi] = newnode;
			return make_pair(iterator(this,newnode,hashi), true);
		}

		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:
		size_t _n = 0;
		vector<Node*> _tables;
	};
}

测试代码

#include"unorderset.h"
#include"unordermap.h"
int main()
{
	
	SF::test_map();
	SF::test_set();
	
	return 0;
}

在这里插入图片描述
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MQTT学习(二)

订阅主题和订阅确认 SUBSCRIBE——订阅主题 之前的CONNECT报文&#xff0c;分为 固定报头&#xff1a;必须存在&#xff0c;用于描述报文信息。里面有指出什么类型的报文&#xff0c;报文的等级。可变报头&#xff1a;不一定存在。主要看什么样子类型的报文。有效载荷部分&a…

Go PDF文件操作

目录 介绍 安装 gofpdf API 代码示例 结果展示 介绍 gofpdf 是一个在 Go 语言中用于生成 PDF 文档的库。 安装 gofpdf 首先&#xff0c;你需要安装 gofpdf 库。你可以使用 go get 命令来安装它&#xff1a; go get github.com/jung-kurt/gofpdf API 功能 函数名参数解释示…

25. K 个一组翻转链表 - 力扣(LeetCode)

基础知识要求&#xff1a; Java&#xff1a;方法、while循环、for循环、if else语句 Python&#xff1a; 方法、while循环、for循环、if else语句 题目&#xff1a; 给你链表的头节点 head &#xff0c;每 k 个节点一组进行翻转&#xff0c;请你返回修改后的链表。 k 是一个…

【目标检测】YOLOv5|YOLOv8模型QT界面可视化部署

YOLO-Deploy-QT_Interface 最近笔者做了YOLO系列算法的部署工作,现做一个总结。主要工作是做了用于部署YOLOv5和YOLOv8的可视化QT界面,可实现图片、文件夹、视频、摄像头的ONNX与OpenVino部署,具体效果如下: 代码链接:https://github.com/Zency-Sun/YOLO-Deploy-QT_Inte…

深入学习Linux内核之v4l2应用编程(二)

一&#xff0c;用户空间访问v4l2设备步骤 V4L2&#xff08;Video for Linux 2&#xff09;是Linux中关于视频设备的内核驱动&#xff0c;它使得Linux系统能够支持视频设备&#xff0c;如摄像头。对于Camera V4L2的应用编程&#xff0c;一般遵循以下步骤&#xff1a; 1&#x…

【正则表达式】1、元字符的认识与分类

1、元字符的概念 正则表达式的常见功能&#xff0c;分别是校验数据的有效性、查找符合要求的文本以及对文本进行切割和替换等操作。 我想你一定在办公软件&#xff0c;比如 Word、Excel 中用过这个功能。你可以使用查找功能快速定位关注的内容&#xff0c;然后使用替换&#xf…

leetcode-最长公共子序列(二)-103

题目要求 思路 step 1&#xff1a;优先检查特殊情况。 step 2&#xff1a;获取最长公共子序列的长度可以使用动态规划&#xff0c;我们以dp[i][j]dp[i][j]dp[i][j]表示在s1中以iii结尾&#xff0c;s2中以jjj结尾的字符串的最长公共子序列长度。 step 3&#xff1a;遍历两个字…

视频号小店从开店到爆单,最详细的攻略教学,来了!

大家好&#xff0c;我是喷火龙 视频号小店从推出到现在一直备受关注&#xff0c;我的团队已经入局视频号小店一年多了&#xff0c; 可以说&#xff0c;新手做视频号小店采用无货源模式和达人带货的玩法依旧是最合适的。 虽然说这个模式和玩法很多人之前都接触过&#xff0c;…

精准追踪,高效分析——Xinstall应用数据分析平台

在当前的移动互联网时代&#xff0c;App应用的数量与日俱增&#xff0c;如何从这些应用中脱颖而出&#xff0c;成为开发者和广告主们亟待解决的问题。而在这个问题中&#xff0c;数据无疑是一把关键的钥匙。今天&#xff0c;我们要介绍的就是国内专业的App全渠道统计服务商——…

普中STM32F103ZET6开发板让DS0和DS1两个LED同时亮

欢迎关注我👆,收藏下次不迷路┗|`O′|┛ 嗷~~ 目录 一.前言 二.代码 三.运行效果 一.前言 在这套stm32教程中,只教学了如何亮DS0,而没有教学如何亮DS1。 二.代码 main.c #include "stm32f10x.h"void Syst

Linux下Redis下载及安装教程(实测有效)

一、配置gcc 由于Redis是基于c语言编写的需要安装依赖,需要安装gcc&#xff0c;在Linux系统里需要存在C语言的编译环境&#xff0c;一般的Linux系统安装的时候会自动安装&#xff0c;由于我是最小安装模式&#xff0c;所以我需要自己再另外安装一下。 判断系统是否安装gcc&…

2024年Java程序员的职业发展路径

程序员的职业路径是非常清晰的&#xff0c;但是现实情况下&#xff0c;很多人卡在了高级开发就再也上不去&#xff0c;直到遇到职业发展的危机&#xff0c;比如&#xff1a; 35岁大龄程序员找工作难&#xff0c;国内很多大型互联网公司在招聘要求上&#xff0c;会限制35岁这个年…

PuLID: 图像背景、光线、风格等均保持高度一致图像生成工具,附本地一键包

PuLID是一种无需调优的ID定制方法。PuLID保持了高的ID保真度&#xff0c;同时有效地减少了对原始模型行为的干扰。 只需要提供一张照片&#xff0c;就可以生成高还原度的各种风格的图像。 使用方法&#xff1a;解压一键包&#xff0c;双击一键启动 点击ID图像&#xff08;主…

李国武:确保FMEA实现预期质量目标的方法有哪些?

在现代制造业中&#xff0c;FMEA&#xff08;失效模式与影响分析&#xff09;已经成为一项至关重要的质量管理工具。它通过对产品或过程进行系统的分析&#xff0c;识别潜在的失效模式&#xff0c;评估其影响&#xff0c;并制定相应的预防措施&#xff0c;从而确保产品或过程的…

Nginx 代理 MySQL 实现通过域名连接数据库

文章目录 Nginx 模块介绍Stream 模块配置远程连接 MySQLDataGrip 连接 MySQL Nginx 安装这里不做介绍。域名默认已经解析到服务器公网IP。 Nginx 模块介绍 HTTP 模块&#xff1a; HTTP模块提供了处理HTTP请求的功能&#xff0c;包括反向代理、负载均衡、缓存、HTTP代理等。 例…

Docker下载镜像出现“missing signature key”如何解决?

“missing signature key” 通常与 Docker 配置有关&#xff0c;具体是 Docker 试图验证镜像的签名但未能找到相应的密钥。这种情况可能发生在启用了 Docker Content Trust (DCT) 的环境中&#xff0c;DCT 是一种安全功能&#xff0c;要求所有镜像必须有签名才能拉取。 原因 …

资料同化 | 搭建docker环境-1

Community Gridpoint Statistical Interpolation (GSI) system DTC 是一个分布式设施&#xff0c;NWP 社区可以在这里测试和评估用于研究和操作的新模型和技术。 DTC的目标包括&#xff1a; 链接研究和操作社区 研究成果转化为实际操作的速度 加快改善天气预报 开发和测试有…

独享静态IP:跨境网络新助手

在数字化浪潮席卷全球的今天&#xff0c;互联网已成为人们生活中不可或缺的一部分。而在这个由数据和信息构成的虚拟世界里&#xff0c;IP地址作为每一个网络设备的独特标识&#xff0c;其重要性不言而喻。特别是独享静态IP&#xff0c;它不仅为用户提供了更加稳定、安全的网络…

在虚机VirtualBox7.0.8安装Androidx86_64系统详细步骤要点

最近需要用到安卓系统蓝牙功能做测试&#xff0c;就选择了Virtualboxandroidx86方案&#xff0c;先把系统安装好&#xff0c;后面看是否可以比较好的完成蓝牙功能测试。如果可以的话&#xff0c;我会再发文分享下的&#xff0c;敬请期待。 1.准备材料 &#xff08;1&#xff…

[数据集][目标检测]交通灯检测数据集VOC+YOLO格式2600张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;2600 标注数量(xml文件个数)&#xff1a;2600 标注数量(txt文件个数)&#xff1a;2600 标注…