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的概念和使用 的相关文章

随机推荐

  • 基于改进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