C++STL剖析(八)—— unordered_set和unordered_multiset的概念和使用

2023-05-16

文章目录

  • 前言
  • 1. unordered_set的介绍和使用
    • 🍑 unordered_set的构造
    • 🍑 unordered_set的使用
      • 🍅 insert
      • 🍅 find
      • 🍅 erase
      • 🍅 size
      • 🍅 empty
      • 🍅 clear
      • 🍅 swap
      • 🍅 count
      • 🍅 迭代器
  • 2. unordered_multiset的介绍和使用
    • 🍑 unordered_multiset的使用
      • 🍅 find
      • 🍅 count


前言

unordered 系列关联式容器

在 C++98 中,STL 提供了底层为红黑树结构的一系列关联式容器,在查询时效率可达到 l o g N logN logN,即最差情况下需要比较红黑树的高度次,当树中的节点非常多时,查询效率也不理想。

最好的查询是,进行很少的比较次数就能够将元素找到,因此在 C++11 中,STL 又提供了 4 个 unordered 系列的关联式容器,这四个容器与红黑树结构的关联式容器使用方式基本类似,只是其底层结构不同。

本文中只对 unordered_mapunordered_set 进行介绍,unordered_multimap 和 unordered_multiset的用法与 multimap 和 multiset 一样,大家可以自行查看文档学习。

在这里插入图片描述

1. unordered_set的介绍和使用

unordered_set 的介绍:

  • unordered_set 是存储没有特定顺序的唯一元素的容器,允许基于它们的值快速检索单个元素。
  • 在 unordered_set 中,元素的值与唯一标识它的键同时存在。键是不可变的,因此,unordered_set 中的元素在容器中不能被修改一次 —— 但是它们可以被插入和删除。
  • 在内部,unordered_set 中的元素不按任何特定顺序排序,而是根据它们的哈希值组织到桶中,以允许直接根据它们的值快速访问单个元素(平均平均时间复杂度恒定)。
  • unordered_set 容器在通过键访问单个元素时比 set 容器更快,尽管它们在通过元素子集进行范围迭代时通常效率较低。
  • 容器中的迭代器至少是前向迭代器。

🍑 unordered_set的构造

构造一个 unordered_set 容器对象,根据使用的构造函数版本初始化其内容,我们主要掌握 3 种方式即可:

在这里插入图片描述

(1)构造一个某个类型的空容器

unordered_set<int> s1; // 构造int类型的空容器

(2)拷贝构造某类型容器

unordered_set<int> us2(us1); // 拷贝构造同类型容器us1的复制品

(3)使用迭代器区间进行初始化构造

构造一个 unordered_set 对象,其中包含范围 [first,last) 中每个元素的副本。

string str("helloworld");
unordered_set<char> us3(str.begin(), str.end()); // 构造string对象某段区间的复制品

🍑 unordered_set的使用

unordered_set 的成员函数主要分为:迭代器,容量操作,修改操作。

需要注意的是,对于 unordered_set 而言,它存储的数据是无序的,并且它是一个单向迭代器。

在这里插入图片描述

我这里只列举几个常用的,其它的可以看 文档 学习。

🍅 insert

在 unordered_set 中插入新元素。

每个元素只有在它不等同于容器中已经存在的任何其他元素时才会被插入,也就是说 unordered_set 中的每个元素是唯一的。

在这里插入图片描述

代码示例

void test_unordered()
{
	unordered_set<int> us1;

	// 插入元素
	us1.insert(4);
	us1.insert(5);
	us1.insert(2);
	us1.insert(2);
	us1.insert(1);
	us1.insert(3);
	us1.insert(3);

	// 遍历
	for (auto e : us1)
	{
		cout << e << " ";
	}
}

可以看到当插入重复元素时,set 是去掉了的,并且没有进行排序。

在这里插入图片描述

🍅 find

在容器中搜索值为 k 的元素,如果找到它,则返回一个迭代器,否则返回 unordered_set::end(容器末端之前的元素)的迭代器。

在这里插入图片描述

代码示例

void test_unordered()
{
	unordered_set<int> us;

	// 插入元素
	us.insert(4);
	us.insert(5);
	us.insert(2);
	us.insert(2);
	us.insert(1);
	us.insert(3);
	us.insert(3);

	unordered_set<int>::iterator pos = us.find(3);
	if (pos != us.end())
	{
		cout << "3存在" << endl;
	}
}

运行结果

在这里插入图片描述

🍅 erase

从 unordered_set 容器中移除单个元素或一组元素([first,last))。

通过调用每个元素的析构函数,这有效地减少了容器的大小。

在这里插入图片描述

(1)从容器中删除单个元素(搭配 find 使用)

void test_unordered()
{
	unordered_set<int> us;

	// 插入元素
	us.insert(4);
	us.insert(5);
	us.insert(2);
	us.insert(2);
	us.insert(1);
	us.insert(3);
	us.insert(3);

	unordered_set<int>::iterator pos = us.find(3);
	if (pos != us.end())
	{
		us.erase(pos); // 删除元素3
		cout << "删除成功" << endl;
	}
	else
	{
		cout << "删除失败" << endl;
	}

	// 遍历
	for (auto e : us)
	{
		cout << e << " ";
	}
}

可以看到元素 3 已经被删除了

在这里插入图片描述

(2)从容器中删除单个元素(直接传要删除的元素)

void test_unordered()
{
	unordered_set<int> us;

	// 插入元素
	us.insert(4);
	us.insert(5);
	us.insert(2);
	us.insert(2);
	us.insert(1);
	us.insert(3);
	us.insert(3);

	us.erase(5); // 删除元素5

	// 遍历
	for (auto e : us)
	{
		cout << e << " ";
	}
}

可以看到 5 已经被删除

在这里插入图片描述

那么它和第 1 种的区别是什么呢?

  • erase(x):如果 x 存在就删除;如果不存在,不做任何改变
  • erase(pos):如果 x 存在就删除;如果不存在,此时 pos 位置指向 set::end 的迭代器,那么程序运行就会报错。

其实这种方式本质上可以理解为 erase 去调用了 迭代器find

🍅 size

返回 unordered_set 容器中的元素数量。

在这里插入图片描述

代码示例

void test_unordered()
{
	unordered_set<string> us;

	// 构造元素
	us = { "milk", "potatoes", "eggs" };
	cout << "size: " << us.size() << endl;

	// 插入元素
	us.insert("pineapple");
	cout << "size: " << us.size() << endl;

	// 插入重复元素
	us.insert("milk");
	cout << "size: " << us.size() << endl;
}

运行结果

在这里插入图片描述

🍅 empty

返回一个 bool 值,指示 unordered_set 容器是否为空,即其大小是否为 0。

这个函数不会以任何方式修改数组的内容。

在这里插入图片描述

代码示例

void test_unordered()
{
	// us1构造3个元素
	unordered_set<string> us1 = { "milk", "potatoes", "eggs" };

	// us2构造一个空容器
	unordered_set<string> us2;

	cout << "us1 " << (us1.empty() ? "is empty" : "is not empty") << endl;
	cout << "us2 " << (us2.empty() ? "is empty" : "is not empty") << endl;
}

运行结果

在这里插入图片描述

🍅 clear

unordered_set 容器中的所有元素都将被删除。

即调用它们的析构函数,并将它们从容器中移除,使容器的大小为 0。

在这里插入图片描述

代码示例

void test_unordered()
{
	unordered_set<string> us = { "chair", "table", "lamp", "sofa" };

	// 遍历
	for (const string& x : us)
	{
		cout << x << " ";
	}
	cout << endl;

	// 清除容器中的所有元素
	us.clear();

	// 再重新插入一些元素
	us.insert("bed");
	us.insert("wardrobe");
	us.insert("nightstand");

	// 遍历
	for (const string& x : us)
	{
		cout << x << " ";
	}
}

运行结果

在这里插入图片描述

🍅 swap

通过 ust 的内容交换容器的内容,ust 是另一个包含相同类型元素的 unordered_set 对象。大小可能不同。

这个函数在容器之间交换指向数据的内部指针,而不实际对单个元素执行任何复制或移动,允许常量时间执行,无论大小如何。

在这里插入图片描述

代码示例

void test_unordered()
{
	unordered_set<string> us1 = { "iron","copper","oil" };
	unordered_set<string> us2 = { "wood","corn","milk" };

	// 交换容器的内容
	us1.swap(us2);

	// 遍历us1
	for (const string& x1 : us1)
	{
		cout << x1 << " ";
	}
	cout << endl;

	// 遍历us2
	for (const string& x2 : us2)
	{
		cout << x2 << " ";
	}
}

运行结果

在这里插入图片描述

🍅 count

在容器中搜索值为 k 的元素,并返回找到的元素数。

因为 unordered_set 容器不允许重复值,这意味着如果容器中存在具有该值的元素,则函数实际返回 1,否则返回 0。

在这里插入图片描述

代码示例

void test_unordered()
{
	unordered_set<string> us = { "hat", "umbrella", "suit" };

	// 容器中值为"hat"的元素个数
	cout << us.count("hat") << endl;

	// 容器中值为"red"的元素个数
	cout << us.count("red") << endl;
}

运行结果

在这里插入图片描述

🍅 迭代器

unordered_set 当中迭代器相关函数如下:

在这里插入图片描述

注意:set 是双向迭代器,而 unordered_set 是单向迭代器

2. unordered_multiset的介绍和使用

unordered_multiset 的介绍:

  • unordered_multiset 是一种容器,它以不特定的顺序存储元素,允许基于它们的值快速检索单个元素,很像 unordered_set 容器,但是允许不同的元素具有等价的值。
  • 在 unordered_multiset 中,元素的值同时是它的键,用于标识它。键是不可变的,因此,unordered_multiset 中的元素在容器中不能被修改一次 —— 但是它们可以被插入和删除。
  • 在内部,unordered_multiset 中的元素不按任何特定顺序排序,而是根据它们的哈希值组织到桶中,以便直接根据它们的值快速访问单个元素(平均时间复杂度恒定)。
  • 具有等效值的元素被分组在同一个桶中,迭代器可以遍历所有这些元素。
  • 容器中的迭代器至少是前向迭代器。

注意,这个容器不是在它自己的头文件中定义的,而是与 unordered_set 共享头文件 <unordered_set>。

🍑 unordered_multiset的使用

unordered_multise 容器与 unordered_set 容器的底层数据结构是一样的,都是哈希表。

其次,它们所提供的成员函数的接口都是基本一致的,这两种容器唯一的区别就是,unordered_multiset 容器允许键值冗余,即 unordered_multiset 容器当中存储的元素是可以重复的。

在这里插入图片描述

代码示例

void test_unordered()
{
	unordered_multiset<int> ums;

	// 插入元素
	ums.insert(4);
	ums.insert(5);
	ums.insert(2);
	ums.insert(2);
	ums.insert(1);
	ums.insert(3);
	ums.insert(3);

	// 遍历
	for (auto e : ums)
	{
		cout << e << " ";
	}
}

可以看到是存在多个相同元素的。

在这里插入图片描述

另外,它和 unordered_set 容器所提供的成员函数的接口都是基本一致的,所以就不全部列举了,只列举几个稍微有点小差别的函数接口。

🍅 find

在容器中搜索以 k 为键的元素,如果找到它,则返回第一个迭代器,否则返回 unordered_multiset::end(容器末尾以上的元素)的迭代器。

  • 要获得一个包含所有键值为 k 的元素的范围,可以使用成员函数 equal_range。
  • 要检查特定键是否存在,可以使用 count。

在这里插入图片描述

代码示例

void test_unordered()
{
	unordered_multiset<int> ums;
	ums.insert(4);
	ums.insert(5);
	ums.insert(6);
	ums.insert(2);
	ums.insert(2);
	ums.insert(1);
	ums.insert(3);
	ums.insert(3);

	// 遍历
	for (auto e : ums)
	{
		cout << e << " ";
	}
	cout << endl;

	// 容器中存在多个2,那么返回第一个2位置的迭代器
	auto pos = ums.find(2); 

	// 打印2位置后面的所有元素(验证pos是否为第一个2位置的迭代器)
	while (pos != ums.end())
	{
		cout << *pos << " ";
		++pos;
	}
}

可以看到确实是从第一个 2 开始打印的

在这里插入图片描述

🍅 count

在容器中搜索值为 k 的元素,并返回找到的元素个数。

在这里插入图片描述

代码示例

void test_unordered()
{
	unordered_multiset<int> ums;

	// 插入元素
	ums.insert(4);
	ums.insert(5);
	ums.insert(2);
	ums.insert(2);
	ums.insert(2);
	ums.insert(2);
	ums.insert(1);
	ums.insert(3);
	ums.insert(3);

	// 统计2的个数
	cout << ums.count(2) << endl;

	// 遍历
	for (auto e : ums)
	{
		cout << e << " ";
	}
}

运行结果

在这里插入图片描述

因为 unordered_multiset 容器允许键值冗余,从上面示例中可以看到,该成员函数 find 和 count 的意义与 unordered_set 容器中的是不同的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++STL剖析(八)—— unordered_set和unordered_multiset的概念和使用 的相关文章

  • 无法在向量向量上使用 emplace_back() 花括号初始化器

    这与我之前提出的有关使用的问题有些相关emplace back在对向量上 将一对插入到 std vector 时 emplace back 与 Push back https stackoverflow com questions 5390
  • 迭代器后继者

    我想用另一个迭代器 同类 的后继者初始化一个迭代器 任意类型 以下代码适用于随机访问迭代器 但不适用于前向或双向迭代器 Iterator i j 1 一个简单的解决方法是 Iterator i j i 但这不起作用初始化语句for 循环的
  • Python 集合上的迭代顺序

    我正在解析两个大文件 GB 大小顺序 每个文件包含keys以及对应的values Some keys在两个文件之间共享 但对应的不同values 对于每个文件 我想写入一个新文件keys 以及对应的values with keys 代表 f
  • 迭代还是使用计数器,这就是问题

    每当有人开始使用 STL 并且他们有一个向量时 您通常会看到 vector
  • 用于列表和映射的 C++ 容器

    我们有一个键和值对的集合 我们需要一个容器 它可以帮助我们检索值 o 1 但也可以记住插入顺序 以便当我们进行迭代时 我们可以像插入顺序一样进行迭代 由于键是一个字符串 我们将无法使用集合或类似的结构 目前我们已经定义了自己的集合类 其中包
  • 使用 istream_iterator 范围构造时无法访问向量

    我尝试编译此代码片段 但出现编译器错误 使用 Visual Studio 2010 进行编译 include
  • android 将自定义字体设置为油漆

    我想在油漆上绘制文字 如何用自定义字体绘制它 前 Helvetica 并且还粗体 我更愿意使用系统字体而不是从资源创建它 谢谢 如果 自定义字体 是指作为资源提供的字体 则以下代码应该有效 Typeface plain Typeface c
  • 二维向量的迭代器

    如何为 2d 向量 向量的向量 创建迭代器 虽然你的问题是not非常清楚 我假设您的意思是 2D 向量表示向量的向量 vector lt vector
  • 如何找到向量中第一个小于整数 X 的元素? (c++)

    如果我有以下向量 10 10 10 20 20 20 30 30 我想要一个函数返回 X 的整数的位置或直接返回 X 之后的较小元素 例如如果我正在搜索 11 我希望函数返回 2 因为第二个元素 10 是第一个较小的元素向量中大于 11 的
  • 在 python 中对自定义类执行集合操作

    我想将 Python 的内置 set 类与我创建的自定义类一起使用 如果我愿意 要创建包含自定义类实例的集合 我需要实现哪些函数才能执行测试 例如 set a set b 它可以开箱即用 但是 在某些情况下 过载是有意义的 eq https
  • 使用accumulate计算数组double[]平均值的函数

    它一定是最常见的函数 每个人在某处都有代码片段 但我实际上花了不少于 1 5 小时在 SO 以及其他 C 网站上搜索它 但还没有找到解决方案 我想计算 a 的平均值double array 使用函数 我想将数组作为函数传递给参考 有数百万个
  • 获得列表并集的最快方法 - Python

    有一个 C 比较可以从列表列表中获取列表的并集 找到集合并集的最快方法 https stackoverflow com questions 11362002 the fastest way to find union of sets 还有其
  • uninitialized_copy memcpy/memmove 优化

    我最近开始研究 MSVC 实现中的 STL 那里有一些不错的技巧 但是我不知道为什么使用以下标准 The std uninitialized copy被优化为一个简单的memcpy memmove如果满足某些条件 据我了解 输入范围可以是m
  • 引用计数指针的STL类?

    这应该是微不足道的 但我似乎找不到它 除非不存在这样的类 智能指针的 STL 类 或类集 是什么 UPDATE 感谢您的回复 我必须说我很惊讶没有标准实施 我最终使用了这个 http archive gamedev net referenc
  • STL(标准模板库)中使用的设计模式

    我正在学习STL和设计模式 我想知道是否有任何文档或链接可以解释如何在 STL 中实现设计模式 我做了谷歌但无法获得太多数据 我希望你的意思是 哪些设计模式可以在STL中识别 STL 堆栈是一个容器适配器 适配器是一种设计模式 迭代器也是一
  • dict_values 视图什么时候可以像设置一样(以及为什么)?

    文档说值视图不被视为类似集合 https docs python org 3 library stdtypes html dictionary view objects 但有时它们是 gt gt gt d 1 1 gt gt gt d va
  • 使用 std::min_element() 时保存函数计算

    假设给你一个 2D 点向量 并期望找到最少的点欧几里得范数 http en wikipedia org wiki Norm 28mathematics 29 Euclidean norm 点提供为std vector
  • C++ STL type_traits 问题

    我正在看最新的C9讲座 http channel9 msdn com Shows Going Deep C9 Lectures Stephan T Lavavej Standard Template Library STL 10 of 10
  • 当另一个进程使用 std::fstream 写入文件时从文件读取[重复]

    这个问题在这里已经有答案了 我需要从文件中逐行读取 它是由 std getline 完成的 另一个进程的问题是一直向其附加数据 然后我需要读取新行 例如 文件一开始包含10行 我的程序读取了10行 那么我的程序应该等待 过了一会儿 另一个进
  • 如何正确删除动画集中引用的 Raphael SVG 元素?

    我有一组动画 Raphael SVG 元素 我正在通过用户发起的 ajax 调用添加新元素并删除旧元素 我 set push 新元素 但因为我需要删除的元素通常不是集合中的最后一个元素 所以我使用 element remove 而不是 se

随机推荐

  • 基于改进SSD算法的小目标检测与应用

    人工智能技术与咨询 点击蓝字 关注我们 来源 xff1a 计算机科学与应用 xff0c 作者刘洋等 关键词 SSD xff1b 深度学习 xff1b 小目标检测 摘要 xff1a 摘要 针对通用目标检测方法在复杂环境下检测小目标时效果不佳
  • Excel线性回归分析

    文章目录 一 学习任务二 学习内容1 1 高尔顿数据集进行线性回归分析1 1 1 父母身高平均值和其中一个子女身高进行回归分析1 1 2 父子身高回归方程1 1 3 母子身高回归方程 1 2 Anscombe四重奏数据集进行回归分析 一 学
  • 组网雷达融合处理组件化设计与仿真

    人工智能技术与咨询 点击蓝色 关注我们 关键词 xff1a 组网雷达 点迹融合 航迹融合 组件化设计 仿真 摘要 数据融合处理是多雷达组网的核心 以典型防空雷达网为参考对象 xff0c 采用组件化设计方式 xff0c 将组网数据融合处理过程
  • 人工智能 知识图谱

    关于举办 2022年数字信息化培训项目系列 知识图谱Knowledge Graph构建与应用研修班线上课程的通知 各有关单位 一 培训目标 本次课程安排紧密结合理论与实践 xff0c 深入浅出 xff0c 循序渐进 从基本概念讲起 xff0
  • 深度学习(Deep Learning)

    知识关键点 1 人工智能 深度学习的发展历程 2 深度学习框架 3 神经网络训练方法 4 卷积神经网络 xff0c 卷积核 池化 通道 激活函数 5 循环神经网络 xff0c 长短时记忆 LSTM 门控循环单元 GRU 6 参数初始化方法
  • 基于深度学习的机器人目标识别和跟踪

    如今 xff0c 深度学习算法的发展越来越迅速 xff0c 并且在图像处理以及目标对象识别方面已经得到了较为显著的突破 xff0c 无论是对检测对象的类型判断 xff0c 亦或者对检测对象所处方位的检测 xff0c 深度学习算法都取得了远超
  • 零基础Linux版MySQL源码方式安装+配置+远程连接完整图解 无坑实录

    无论开发还是运维 xff0c 项目环境搞不定 xff0c 还真让你干不成活 xff0c MySQL在不同场景 不同平台下安装方式也不同 xff0c 本次主要分享centos7下MySQL源码rpm方式安装 xff0c 其它方式后续分享 xf
  • C++,友元,语法+示例,非常详细!!!!

    友元概念 友元的目的就是让一个函数或者类 访问另外一个类中的私有成员 友元的关键字为 friend 友元的几种实现 全局函数做 友元类做 友元成员函数做 友元重载函数做 友元 全局函数做 友元 include lt iostream gt
  • STL——STL简介、STL六大组件

    一 STL是什么 STL standard template library xff1a C 43 43 标准模板库 xff0c 是C 43 43 标准库的重要组成部分 xff0c 不仅是一个可复用的组件库 xff0c 还是一个包罗数据结构
  • 文件流指针和文件描述符

    1 文件流指针和文件描述符的产生 fopen函数打开文件成功后会返回文件流指针 open函数打开文件成功后返回的是文件描述符 他俩的相同点是通过文件流指针和文件描述符都可以对文件进行操作 2 fopen函数和open函数的介绍 fopen函
  • docker 操作

    查看容器 xff1a sudo docker ps a 删除容器 xff1a sudo docker rm NAMES 容器的名字 下载镜像 xff1a sudo docker pull rmus2022 server v1 2 0 查看镜
  • 树莓派32位系统烧录及连接

    目录 前言 一 烧录树莓派系统 1 格式化tf卡 2 烧录系统 二 连接树莓派 1 开启SSH 2 开启网络共享 3 下载Putty 三 开启图形化界面 非必须 最后 xff1a 前言 我在树莓派环境搭建的过程中 xff0c 看了几十篇博客
  • 鸢尾花Iris数据集进行SVM线性分类

    文章目录 一 学习任务二 学习内容1 鸢尾花数据集使用SVM线性分类1 1 SVM介绍1 2 LinearSVC xff08 C xff09 方式实现分类1 3 分类后的内容基础上添加上下边界 三 参考博客 一 学习任务 安装python3
  • intel realsense d435i相机标定中文文档

    intel realsense d435i相机标定中文文档 此文档参考了官方的英文文档 xff0c 原地址面向英特尔 实感 深度摄像头的 IMU 校准工具 intelrealsense com IMU概述 xff1a 惯性测量单元 imu
  • VScode-git提交 无法推送refs到远端

    在将代码同步到远端仓库时 xff0c 弹窗提醒 无法推送refs到远端 您可以试着运行 拉取 功能 xff0c 整合您的更改 但尝试后发现 拉取 功能也无法解决问题 xff0c 最后是因为文件过大原因 xff0c 在这里记录一下解决方法 x
  • VMware16虚拟机中安装OpenEuler详细教程指南

    文章目录 安装前提准备镜像创建虚拟机安装欧拉踩坑指南 x1f351 网络指南 安装前提 Windown 10VMware 16openEuler 20 03 LTS SP3 准备镜像 镜像地址 xff1a OpenEuler 直接在官网下载
  • C/C++排序算法(三)—— 冒泡排序和快速排序

    文章目录 前言1 冒泡排序 x1f351 基本思想 x1f351 图解冒泡 x1f351 动图演示 x1f351 代码实现 x1f351 代码优化 x1f351 特性总结 2 快速排序 x1f351 hoare 版本 x1f345 图解过程
  • C/C++排序算法(四)—— 归并排序和计数排序

    文章目录 前言1 归并排序 x1f351 基本思想 x1f351 算法图解 x1f345 分组 x1f345 归并 x1f345 比较 x1f351 动图演示 x1f351 代码实现 x1f351 非递归实现 x1f345 情况一 x1f3
  • C++深入浅出(九)—— 多态

    文章目录 1 多态的概念2 多态的定义及实现 x1f351 多态的构成条件 x1f351 虚函数 x1f351 虚函数的重写 x1f351 虚函数重写的两个例外 x1f351 C 43 43 11的override 和 final x1f3
  • C++STL剖析(八)—— unordered_set和unordered_multiset的概念和使用

    文章目录 前言1 unordered set的介绍和使用 x1f351 unordered set的构造 x1f351 unordered set的使用 x1f345 insert x1f345 find x1f345 erase x1f3