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

news2024/11/26 9:07:22

文章目录

  • 前言
  • 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 容器中的是不同的。

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

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

相关文章

安全多方计算系列笔记1——前世今生

这一系列笔记参考了绿盟科技研究通讯的安全多方计算文章&#xff0c;及其他。 首先看定义&#xff1a;在不泄露参与方原始输入数据的前提下&#xff0c;允许分布式参与方合作计算任意函数&#xff0c;输出准确的计算结果。 起源 安全多方计算问题及解首先由姚期智&#xff08…

用大白话给你科普,到底什么是 API(应用程序编程接口)?

何为API&#xff1f;如果你在百度百科上搜索&#xff0c;你会得到如下结果&#xff1a;API&#xff08;Application Programming Interface&#xff0c;应用程序编程接口&#xff09;是一些预先定义的函数&#xff0c;目的是提供应用程序与开发人员基于某软件或硬件得以访问一组…

Linux 安装Python3

python3 下载地址 python3 下载地址 https://www.python.org/downloads/ 选择自己需要的版本、 此文中选择3.10.9 下载源码压缩包 可下载到本地后上传至Linux服务器也可以复制下载地址 wget https://www.python.org/ftp/python/3.10.9/Python-3.10.9.tgzpython3 安装 yum…

python练习——简化路径

项目场景&#xff1a; 给你一个字符串 path &#xff0c;表示指向某一文件或目录的 Unix 风格 绝对路径 &#xff08;以 /开头&#xff09;&#xff0c;请你将其转化为更加简洁的规范路径。在 Unix 风格的文件系统中&#xff0c;一个点&#xff08;.&#xff09;表示当前目录本…

新库上线 | CnOpenData中国地方政府债券信息数据

中国地方政府债券信息数据 一、数据简介 地方政府债券 指某一国家中有财政收入的地方政府地方公共机构发行的债券。地方政府债券一般用于交通、通讯、住宅、教育、医院和污水处理系统等地方性公共设施的建设。地方政府债券一般也是以当地政府的税收能力作为还本付息的担保。地…

【电源专题】案例:用电子负载模拟电池测试充电芯片

最近在做有关充电芯片的选型,所以需要测试充电芯片中的很多参数。如涓流充电电流、快速充电电流、截止电流等等。 如下所示为某充电IC充电过程中的电流电压变化曲线。可以看出其中存在多个电流如Ishort/Iterm/Iprechg/Ichgerg等等。电压点也有Vshortz/Vbatlow/Vbatreg等。这些…

VTK- vtkStripper

前言&#xff1a;本博文主要研究接口vtkStripper的实现原理及主要的应用场景&#xff0c;希望对各位小伙伴有所帮助。 描述&#xff1a;vtkStripper是一个过滤器&#xff0c;用于从输入多边形、三角形带和线中生成三角形带和/或折线。输入多边形仅当它们是三角形时才被组装成三…

【Linux】-- 进程信号(处理、内核)

上篇&#xff1a;【Linux】-- 进程信号&#xff08;认识、应用&#xff09;_川入的博客-CSDN博客 目录 信号其他相关常见概念 pending handler block 信号处理的过程 sigset_t sigset_t使用 系统接口 sigpending sigprocmask 捕捉方法 sigaction struct sigactio …

小白入门模拟IC设计,如何快速学习?

众所周知&#xff0c;模拟电路很难学。以最普遍的晶体管来说&#xff0c;我们分析它的时候必须首先分析直流偏置&#xff0c;其次在分析交流输出电压。可以说&#xff0c;确定工作点就是一项相当麻烦的工作&#xff08;实际中来说&#xff09;&#xff0c;晶体管的参数多、参数…

JavaScript 计时事件

JavaScript 计时事件 通过使用 JavaScript&#xff0c;我们有能力做到在一个设定的时间间隔之后来执行代码&#xff0c;而不是在函数被调用后立即执行。我们称之为计时事件。 在 JavaScript 中使用计时事件是很容易的&#xff0c;两个关键方法是: setInterval() - 间隔指定的…

【CNN记录】tensorflow中depth_to_space

功能把depth维的数据移到space上&#xff0c;与spacetodepth刚好是相反的操作&#xff0c;depth对应channel&#xff0c;space对应height和width&#xff0c;而该操作是把depth上的数据分给height和width上&#xff0c;所以对应有一个参数block_size&#xff0c;要求原tensor的…

CSDN竞赛28期参赛体验

1、小Q的鲜榨柠檬汁 1、题目名称&#xff1a;小Q的鲜榨柠檬汁 团建活动是大家所想要的。 小Q给大家准备了鲜橙汁。 现在有n个朋友买回了k瓶饮料&#xff0c;每瓶有l毫升的饮料&#xff0c;同时还买回 了c个柠檬&#xff0c; 每个柠檬可以切成d片&#xff0c;p克盐。 已知每个朋…

Python基础学习笔记 —— 数据结构与算法

数据结构与算法1 数据结构基础1.1 数组1.2 链表1.3 队列1.4 栈1.5 二叉树2 排序算法2.1 冒泡排序2.2 快速排序2.3 &#xff08;简单&#xff09;选择排序2.4 堆排序2.5 &#xff08;直接&#xff09;插入排序3 查找3.1 二分查找1 数据结构基础 本章所需相关基础知识&#xff1a…

第七届蓝桥杯省赛——1有奖猜谜

题目&#xff1a; 小明很喜欢猜谜语。 最近&#xff0c;他被邀请参加了X星球的猜谜活动。 每位选手开始的时候都被发给777个电子币。 规则是&#xff1a;猜对了&#xff0c;手里的电子币数目翻倍&#xff0c; 猜错了&#xff0c;扣除555个电子币, 扣完为止。 小明一共猜了15…

入门深度学习——基于全连接神经网络的手写数字识别案例(python代码实现)

入门深度学习——基于全连接神经网络的手写数字识别案例&#xff08;python代码实现&#xff09; 一、网络构建 1.1 问题导入 如图所示&#xff0c;数字五的图片作为输入&#xff0c;layer01层为输入层&#xff0c;layer02层为隐藏层&#xff0c;找出每列最大值对应索引为输…

云原生周刊 | 开源领导者应该如何应对碎片化挑战?

Linux Fundation 发布了一份关于开源开发中的碎片化问题的报告《实现全球协作&#xff1a;开源领导者如何应对碎片化挑战》&#xff0c;该报告由华为在美国的研发部门 Futurewei 赞助。报告指出&#xff0c;虽然开源社区越来越国际化&#xff0c;但美国对开源共享和开发进行了过…

源码项目中常见设计模式及实现

原文https://mp.weixin.qq.com/s/K8yesHkTCerRhS0HfB0LeA 单例模式 单例模式是指一个类在一个进程中只有一个实例对象&#xff08;但也不一定&#xff0c;比如Spring中的Bean的单例是指在一个容器中是单例的&#xff09; 单例模式创建分为饿汉式和懒汉式&#xff0c;总共大概…

Linux内核驱动开发(一)

Linux内核初探 linux操作系统历史 开发模式 git 分布式管理git clone 获取git push 提交git pull 更新 邮件组 mailing list patch 内核代码组成 Makfile arch 体系系统架构相关 block 块设备 crypto 加密算法 drivers 驱动&#xff08;85%&#xff09; atm 通信bluet…

MAC文件误删怎么办?mac数据恢复,亲测很好用的方法

电脑文件误删&#xff0c;应该很多人都经历过。之前分享了很多关于Windows电脑文件误删如何恢复的方法&#xff0c;那么MAC电脑文件误删该怎么办&#xff1f;有什么好方法可以使得mac数据恢复回来吗&#xff1f;下面就给大家分享一些亲测好用的方法&#xff01; 一、MAC电脑的文…

使用Proxifier+burp抓包总结

一、微信小程序&网页抓包 1. Proxifier简介 Proxifier是一款功能非常强大的socks5客户端&#xff0c;可以让不支持通过代理服务器工作的网络程序能通过HTTPS或SOCKS代理或代理链。 2. 使用Proxifier代理抓包 原理&#xff1a;让微信相关流量先走127.0.0.1:80到burp。具体…