C++ unordered_map

news2024/11/16 3:33:49

1. unordered系列关联式容器

在C++98 中, STL 提供了底层为红黑树结构的一系列关联式容器,在查询时效率可达到  \log_{2}N ,即最差情况下需要比较红黑树的高度次,当树中的节点非常多时,查询效率也不理想。最好的查询是,进行很少的比较次数就能够将元素找到,因此在C++11 中, STL 又提供了 4 个unordered系列的关联式容器,这四个容器与红黑树结构的关联式容器使用方式基本类似,只是其底层结构不同,该系列容器使用哈希表进行封装。

2. unordered_map的介绍

1. unordered_map 是存储 <key, value> 键值对的关联式容器,其允许通过 key 快速的索引到与其对应的value
2. unordered_map 中,键值通常用于惟一地标识元素,而映射值是一个对象,其内容与此键关联。键和映射值的类型可能不同。
3. 在内部 ,unordered_map 没有对 <kye, value> 按照任何特定的顺序排序 , 为了能在常数范围内找到key 所对应的 value unordered_map 将相同哈希值的键值对放在相同的桶中。
4. unordered_map 容器通过 key 访问单个元素要比 map 快,但它通常在遍历元素子集的范围迭代方面效率较低。
5. unordered_maps 实现了直接访问操作符 (operator[ ]) ,它允许使用 key 作为参数直接访问value。
6. 它的迭代器至少是前向迭代器。

3. unordered_map的使用

1. unordered_map的构造

函数声明功能介绍
unordered_map()
构造不同格式的 unordered_map 对象

2. unordered_map的容量

函数声明
功能介绍
bool empty() const
检测 unordered_map 是否为空
size_t size() const
获取 unordered_map 的有效元素个数

3. unordered_map的迭代器

函数声明 功能介绍
iterator  begin()
返回 unordered_map 第一个元素的迭代器
iterator end()
返回 unordered_map 最后一个元素下一个位置的迭代器
const_iterator cbegin() const
返回 unordered_map 第一个元素的 const 迭代器
const_iterator cend() const
返回 unordered_map 最后一个元素下一个位置的 const 迭代器

4. unordered_map的元素访问

函数声明功能介绍
mapped_type& operator[ ] (const key_type& k)
返回与 key 对应的 value ,没有返回一个默认值
注意:该函数中实际调用哈希桶的插入操作,用参数 key V() 构造一个默认值往底层哈希桶中插入,如果key 不在哈希桶中,插入成功,返回 V() ,插入失败,说明 key 已经在哈希桶中,将key 对应的 value 返回。

5. unordered_map的查询

函数声明功能介绍
iterator find(const K& key)
返回 key 在哈希桶中的位置
size_t count(const K& key)
返回哈希桶中关键码为 key 的键值对的个数
注意: unordered_map key 是不能重复的,因此 count 函数的返回值最大为 1

6. unordered_map的修改操作

函数声明
功能介绍
pair<iterator,bool> insert (const value_type& x )
向容器中插入键值对

size_type erase ( const key_type& x )

删除容器中的键值对
void clear()
清空容器中有效元素个数
void swap(unordered_map&)
交换两个容器中的元

7. unordered_map的桶操作

函数声明
功能介绍
size_t bucket_count()const
返回哈希桶中桶的总个数
size_t bucket_size(size_t n)const
返回 n 号桶中有效元素的总个数
size_t bucket(const K& key)
返回元素 key 所在的桶号

4.unordered_map的模拟实现

首先我们要使用哈希表进行封装unordered_map即可,如下是HashTable.cpp的文件,有关哈希表的详细介绍,可以点击了解C++哈希表。

#include <iostream>
#include <vector>
using namespace std;
namespace OpenHash
{
	template<class T>
	struct HashNode
	{
		T _data;
		HashNode* _next;
		HashNode(const T& data)
			:_data(data)
			, _next(nullptr)
		{}
	};
	//将key转换为整型方便取模
	template <class K>
	struct Hash
	{
		size_t operator()(const K& key)
		{
			return key;
		}
	};
	//模板特化,将string类型转换为整型
	template<>
	class Hash<string>
	{
		size_t operator()(const string& s)
		{
			size_t ret = 0;
			for (auto e : s)
			{
				ret = ret * 31 + e;
			}
			return ret;
		}
	};
	//实现迭代器
	//因为迭代器的实现需要借助HashTable,所以需要前置定义
	template <class K, class T, class KeyOfT, class HashFunc = Hash<K>>
	class HashTable;
	template <class K, class T, class Ptr, class Ref, class KeyOfT, class HashFunc = Hash<K>>
	struct HashTableIterator
	{
		typedef typename HashNode<T> Node;
		typedef typename HashTableIterator<K, T, Ptr, Ref, KeyOfT, HashFunc> Self;
		typedef typename HashTable<K, T, KeyOfT, HashFunc> HashTable;

		HashTable* _ht;
		Node* _node;
		HashTableIterator() = default;
		HashTableIterator(const Node*& node, const HashTable*& ht)
			:_ht(ht)
			, _node(node)
		{}
		Self& operator++()
		{
			//遍历当前桶
			if (_node->_next)
				_node = _node->_next;
			//找下一个桶
			else
			{
				KeyOfT kot;
				HashFunc hf;
				//获取索引值
				size_t index = hf(kot(_node->_data)) % _ht->_table.size();
				++index;
				while (index < _ht->_table.size() && _ht->_table[index] == nullptr)
					++index;
				if (index == _ht->_table.size())
					_node = nullptr;
				else
					_node = _ht->_table[index];
			}
			return *this;
		}
		Ref operator*()
		{
			return _node->_data;
		}
		Ptr operator->()
		{
			return &_node->_data;
		}
		bool operator==(const Self& s)
		{
			return _node == s._node;
		}
		bool operator!=(const Self& s)
		{
			return _node != s._node;
		}
	};

	template <class K, class T, class KeyOfT, class HashFunc>
	class HashTable
	{
	public:
		//友元(因为iterator需要用到_table)
		template <class K, class T, class Ptr, class Ref, class KeyOfT, class HashFunc>
		friend struct HashTableIterator;
		typedef typename HashNode<T> Node;
		typedef typename HashTableIterator<K, T, T*, T&, KeyOfT, HashFunc> iterator;
		//构造函数
		HashTable()
			:_table(vector<Node*>())
			, _n(0)
		{}
		iterator begin()
		{
			for (size_t i = 0; i < _table.size(); i++)
			{
				if (_table[i])
					return iterator(_table[i], this);
			}
			return iterator(nullptr, this);
		}
		iterator end()
		{
			return iterator(nullptr, this);
		}
		iterator find(const K& key)
		{
			if (_table.size() == 0)
			{
				return iterator(nullptr, this);
			}
			KeyOfT kot;
			HashFunc hf;
			size_t index = hf(key) % _table.size();
			Node* cur = _table[index];
			while (cur)
			{
				if (kot(cur->_data) == key)
					return iterator(cur, this);
				cur = cur->_next;
			}
			return iterator(nullptr, this);
		}
		pair<iterator, bool> insert(const K& key)
		{
			//第一次插入需要扩容
			if (_table.size() == 0)
				_table.resize(10);
			//不能出现重复数据
			if (find(key) != iterator(nullptr, this))
			{
				return make_pair(find(key), false);
			}
			KeyOfT kot;
			HashFunc hf;
			//桶的个数是一定的,随着元素的不断插入,每个桶中元素的个数不断增多,极端情况下,
			//可能会导致一个桶中链表节点非常多,会影响的哈希表的性能,因此在一定条件下需要对
			//哈希表进行增容,那该条件怎么确认呢?开散列最好的情况是:每个哈希桶中刚好挂一个
			//节点,再继续插入元素时,每一次都会发生哈希冲突,因此,在元素个数刚好等于桶的个数
			//时,可以给哈希表增容。
			//负载因子到1,需要扩容
			if (_n == _table.size())
			{
				vector<Node*> newtable;
				newtable.resize(_table.size() * 2);
				//重新映射到新表
				for (auto e : _table)
				{
					Node* cur = e;
					while (cur)
					{
						size_t index = hf(kot(cur->_data)) % newtable.size();
						cur->_next = newtable[index];
						newtable[index] = cur;
						cur = cur->_next;
					}
				}
			}
			size_t index = hf(key) % _table.size();
			Node*& cur = _table[index];
			while (cur)
				cur = cur->_next;
			cur = new Node(key);
			return make_pair(iterator(cur, this), true);
		}
		bool erase(const K& key)
		{
			KeyOfT kot;
			HashFunc hf;
			size_t index = hf(key) % _table.size();
			Node* cur = _table[index], * pre = _table[index];
			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					//要删除该位置第一个元素
					if (cur == pre)
						_table[index] = cur->_next;
					else
						pre->_next = cur->_next;

					delete cur;
					_n--;
					return true;
				}
				pre = cur;
				cur = cur->_next;
			}
			return false;
		}
	private:
		vector<Node*> _table;
		size_t _n;//有效数据个数
	};
}

unordered_map.cpp文件如下

#include"HashTable.cpp"
namespace lbk
{
	template<class K,class Hash=OpenHash::Hash<K>>
	class unordered_set
	{
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
	public:
		typedef typename OpenHash::HashTable<K,K,SetKeyOfT,Hash>::iterator iterator;
		iterator begin()
		{
			return _ht.begin();
		}
		iterator end()
		{
			return _ht.end();
		}
		iterator find(const K& key)
		{
			return _ht.find(key);
		}
		pair<iterator, bool> insert(const K& key)
		{
			return _ht.insert(key);
		}
		bool erase(const K& key)
		{
			return _ht.erase(key);
		}
	private:
		OpenHash::HashTable<K, K, SetKeyOfT, Hash> _ht;
	};
}

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

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

相关文章

亚马逊VC账号特权及SC升级至VC策略解析——WAYLI威利跨境助力商家

亚马逊的VC(Vendor Central)账号和SC(Selling on Amazon)账号在运营策略上有显著差异&#xff0c;这些差异主要体现在账号安全性、数据获取、广告投放以及费用结构等方面。 VC账号特权 1、高安全性&#xff1a;VC账号相较于SC账号具有更高的安全性。由于亚马逊平台对不同卖家账…

【Python数据结构与算法】递归----上台阶

题目&#xff1a;上台阶 描述 有n级台阶&#xff08;0<n<20&#xff09;&#xff0c;从下面开始走要走到所有台阶上面&#xff0c;每步可以走一级或两级&#xff0c;问有多少种不同的走法。 输入 一个整数n 输出 走法总数 样例输入 4样例输出 5AC代码 def ways(n): …

前端-如何通过docker打包Vue服务成镜像并在本地运行(本地可以通过http://localhost:8080/访问前端服务)

1、下载安装docker&#xff0c;最好在vs code里安装docker的插件。 下载链接&#xff1a;https://www.docker.com/products/docker-desktop &#x1f389; Docker 简介和安装 - Docker 快速入门 - 易文档 (easydoc.net) 2、准备配置文件-dockerfile文件和nginx.conf文件 do…

YOLO:训练自己的样本数据集进行目标检测

作者&#xff1a;CSDN _养乐多_ 本文将介绍如何使用python语言和 ultralytics 库训练自己的数据集&#xff0c;并进行 YOLO 目标检测模型训练和推理的代码。 文章目录 一、样本数据集准备1.1 标注工具1.2 数据集格式1.2.1 图片和标签数据集制作1.2.2 data.yaml制作 二、模型训…

SAP 采购订单审批 Flexible Workflow

目录 1 简介 2 业务数据 1&#xff09;下采购订单&#xff0c;如果订单金额超过 15w 生成 Flexible Workflow 审批 2&#xff09;审批采购订单 - 系统默认主页显示需要审批的采购订单&#xff0c;也可以设置成发邮件提醒 3 后台配置 4 前台主数据定义 1&#xff09;创建…

【初学人工智能原理】【9】深度学习:神奇的DeepLearning

前言 本文教程均来自b站【小白也能听懂的人工智能原理】&#xff0c;感兴趣的可自行到b站观看。 代码及工具箱 本专栏的代码和工具函数已经上传到GitHub&#xff1a;1571859588/xiaobai_AI: 零基础入门人工智能 (github.com)&#xff0c;可以找到对应课程的代码 正文 深度…

14.按钮和多选框

<p>爱好&#xff1a;<input type"checkbox" value"Riding" name"hobby">骑行<input type"checkbox" value"experiment" name"hobby">做实验<input type"checkbox" value"lea…

牛客JS题(十六)生成页码

注释很详细&#xff0c;直接上代码 涉及知识点&#xff1a; 合法性判断JS除法的特点 题干&#xff1a; 我的答案 <!DOCTYPE html> <html><head><meta charsetutf-8></head><body><ul id"ul"></ul><script type&…

Vsftp 源码安装部署(CentOS 8)

概述 运行环境是 CentOS 8.2 ,尝试搭建 ftp 服务&#xff0c;网上找了很多的方式&#xff0c;基本都是 yum 安装&#xff0c;但是因为未连接互联网&#xff0c;因此只能源码安装。 rpm 包下载地址&#xff08;无效&#xff09; RPM resource vsftpd 基本都是针对 CentOS 9 以…

RV1103调用摄像头运行yolov5进行实时检测

目录 前言运行Yolov5模型第一步&#xff1a;修改CMA_SIZE第二步&#xff1a;修改yolov5项目代码第三步&#xff1a;运行程序 前言 买了luckfox pico的rv1103开发板&#xff0c;摄像头是SC3336 3MP Camera (A)摄像头&#xff0c;参考RV1103 Luckfox Pico使用SPI NAND Flash烧录…

数据库备份与恢复和日志管理

一、数据库备份的概述 1、数据库备份的目的&#xff1a;备灾。在生产环境中&#xff0c;数据的安全性非常重要 2、造成数据丢失的原因&#xff1a;程序出错、人为的问题、磁盘故障、自然灾害。 二、备份的分类 从物理和逻辑的角度 1、物理备份&#xff1a; 对数据库操作系…

可视掏耳勺安全吗?独家揭示六大风险弊病!

很多人习惯在洗漱完顺手拿一根棉签掏耳朵&#xff0c;但是棉签的表面直径大且粗糙&#xff0c;不易将耳朵深处的耳垢挖出&#xff0c;耳垢堆积在耳道深处长时间不清理会导致堵塞耳道&#xff0c;引起耳鸣甚至感染。而可视掏耳勺作为一种新型的挖耳工具&#xff0c;它的安全性也…

OpenAI突然上线两件“杀手锏”:势在维持大模型霸主地位

在最近的大模型战争中&#xff0c;OpenAI似乎很难维持霸主地位。虽然没有具体的数据统计&#xff0c;但Claude3.5出现后&#xff0c;只是看网友们的反响&#xff0c;就能感觉到OpenAI订阅用户的流失&#xff1a;既然Claude3.5比GPT-4o好用&#xff0c;为什么我们不去订阅Claude…

html+css 实现遮罩按钮

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享htmlcss 绚丽效果&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 文…

mybatis-plus批量插入优化

mybatis-plus批量插入优化 背景优化新的问题分批插入springboot3整合mybaits-plus 背景 使用的mybatisplus的批量插入方法&#xff1a;saveBatch()&#xff0c;打印 sql 日志发现&#xff0c;底层还是一条条的 insert 语句&#xff0c;这显然是不行的 优化 之前就看到过网上都…

怎么把C盘分成两个盘?让C盘分区更简单,赶快试试!

在日常使用电脑的过程中&#xff0c;有时我们可能希望将C盘分割成两个独立的分区&#xff0c;以便更好地管理文件和数据。这种操作需要谨慎进行&#xff0c;因为错误的分区操作可能导致数据丢失。那么&#xff0c;我们该怎么把C盘分成两个盘呢&#xff1f;下面&#xff0c;我将…

Telegraf 命令行指南:高效监控数据的秘诀

Telegraf 是一个轻量级的服务器监控代理&#xff0c;它支持从数百种数据源收集、处理和发送数据到各种存储库。它由 InfluxData 开发&#xff0c;常用于时间序列数据库 InfluxDB。Telegraf 的灵活性和强大的插件系统使其成为监控基础设施的理想选择。本文将为您提供一个 Telegr…

【解决方案】华普微基于收发芯片系列的LED智能灯控高效解决方案

一、方案概述 LED智能灯是一种集LED照明技术与智能控制技术于一体的现代照明产品。它采用高效节能的LED作为光源&#xff0c;相比传统灯具&#xff0c;具有更低的能耗、更长的使用寿命以及更环保的特性。 智能灯通过内置的智能芯片或连接外部智能设备&#xff08;如智能手机、…

vue2-级联选择器

级联选择器 一、市面上的级联选择器二、功能实现1、数据类型2、隐藏下拉框的方法3、html结构4、CSS代码5、各个方法代码 三、实现样式截图1、一级菜单1、鼠标放到一级菜单&#xff0c;就显示二级菜单2、鼠标点击一级菜单的时候 2、二级菜单1、鼠标放到二级菜单的时候&#xff0…

合规征程新里程碑:ATFX荣获香港SFC牌照,运营再上新台阶

全球知名金融科技品牌ATFX又传来好消息&#xff0c;继2023年6月获得阿联酋SCA第五类牌照后&#xff0c;ATFX全球合规运营策略再次取得重大成功。日前&#xff0c;ATFX宣布获得中国香港证券及期货事务监察委员会&#xff08;SFC&#xff09;颁发的第三类牌照&#xff08;号码&am…