详解c++---set的介绍

2023-11-09

set容器的介绍

set容器可以看成我们上一篇文章学习的K结构的搜索二叉树,所以set容器不仅可以存储数据,还可以对数据进行排序和去重,另外set属于关联式容器也就是说set的每个元素都是通过某种关系相互链接起来的不想vector一样在虚拟地址上一个元素紧接着一个元素,所以set容器种没有push_bck函数只有insert函数,那么接下来我们就来看看insert函数的介绍。
在这里插入图片描述

set的构造函数

在这里插入图片描述
set容器提供了三种构造函数我们首先来看第一种,这种构造函数有两个参数并且每个参数都有一个缺省值,那么第一个参数表示的意思就是搜索二叉树的比较方法,如果库中提供的够用的话这个参数就不需要传,第二个参数就是一个有关内存池的参数,那么这个参数对于我们初学者也不需要管,所以第一种构造函数对于我们来说几乎什么参数都不用传,直接用它创建一个空容器就可以了,比如说下面的代码:

void func()
{
	set<int> tmp1;
	set<long long> tmp2;
	set<char> tmp3;
	set<double> tmp4;
}

那么第二种的形式有两个迭代器参数,那么这种形式表示的意思就是用迭代器范围的内容构造一个容器,比如说下面的代码:

void func()
{
	vector<int> v = { 1,2,3,4,5 };
	set<int> tmp(v.begin(), v.end());
}

那么这种形式创建的容器在一开始就会有数据:
在这里插入图片描述
第三种形式就是拷贝构造函数,这种形式就不必多讲了,大家可以看看下面的代码:

void func()
{
	vector<int> v = { 1,2,3,4,5 };
	set<int> tmp(v.begin(), v.end());
	set<int> copy_tmp(tmp);
}

这里通过调试就可以看到两个容器里面的内容是一样的:
在这里插入图片描述
那么这就是set的构造函数,很简单跟之前学的差不多。

insert函数的介绍

在这里插入图片描述
首先参数中的value_type就是模板参数中的一个参数的重命名,那么知道这个之后我们就可以明白第一个形式的insert函数表示的意思就是往容器中插入一个T类型的数据,比如说下面的代码:

void func1()
{
	set<int> tmp;
	tmp.insert(2);
	tmp.insert(1);
	tmp.insert(3);
	tmp.insert(0);
}

因为名为tmp创建的时候显示实例化的类型是int,所以我们可以通过insert函数向里面插入int类型的数据,第二种形式的参数多了一个迭代器类型的参数,那么这种形式表示的意思就是往容器中的指定位置插入数据,那么这种形式希望大家谨慎使用因为可能会导致树的结构发生破坏,使其失去搜索二叉树的特征。第三种形式的参数就是两个迭代器,那么这种形式的insert函数表示的意思就是将一个范围内的数据插入到容器里面,那么这个迭代器类型可以是任意容器比如说下面的代码:

void func1()
{
	set<int> tmp1;
	vector<int> v = { 1,3,5,7,9 };
	list<int> l = { 2,4,6,8,10 };
	tmp1.insert(v.begin(), v.end());
	tmp1.insert(l.begin(), l.end());
}

那么这里我们可以通过调试查看tmp1中的数据内容:
在这里插入图片描述
那么这就是set的insert函数的第三个形式的作用希望大家能够理解。

find函数

find函数想必大家都应该知道这个函数的作用,那么这里我们首先来看看这个函数的参数形式:
在这里插入图片描述
find函数只有一个形式,这种形式的意思传递你要找的数据就行,如果找到的话这里会返回这个数组所在的容器中的位置,如果找不到的话这里会返回一个指向容器结尾end的迭代器,比如说下面的代码:

void func2()
{
	vector<int> v = { 1,3,5,7,9 };
	set<int> tmp(v.begin(),v.end());
	set<int> ::iterator it = tmp.find(3);
	if (it != tmp.end())
	{
		cout << "找到了数据为:" << *it << endl;
	}
	else
	{
		cout << "容器中没有这个数据" << endl;
	}

}

这段代码的运行结果如下:
在这里插入图片描述
如果我们要找一个不存在的数据的话这里运行的结果如下,比如说把上面的3改成4运行的结果就如下:
在这里插入图片描述
那么这里就有一个问题,set库中提供了find函数,但是算法库中也提供了find函数,并且这个函数都可以达到我们的预期,比如说下面的代码:

void func2()
{
	vector<int> v = { 1,3,5,7,9 };
	set<int> tmp(v.begin(),v.end());
	set<int> ::iterator it = find(tmp.begin(), tmp.end(), 3);
	if (it != tmp.end())
	{
		cout << "找到了数据为:" << *it << endl;
	}
	else
	{
		cout << "容器中没有这个数据" << endl;
	}

}

这段代码的运行结果如下:
在这里插入图片描述

那我们用哪个find函数好呢?能不能为了统一就只是用算法库中的find函数呢?答案是最好不要因为算法库中的find函数是暴露查找通过循环逐个比对迭代器中的值是否和要找的值相匹配,这种查找方式并没有发挥搜索二叉树所特有的性质所以不推荐使用,而set库中的find函数充分的利用了二分查找的性质,所以他的效率会更高,我们更推荐使用,那么这就是两个find函数的区别,虽然都能达到目的,但是set的效率会更高。

erase函数

我们来看看erase函数的参数形式:
在这里插入图片描述

erase函数支持三种参数的删除,第一种的参数就是一个迭代器表示的意思就是将迭代器指向的位置的元素进行删除,比如说下面的代码:

void func3()
{
	vector<int> v = { 2,1,5,4,3 };
	set<int> tmp(v.begin(), v.end());
	set<int>::iterator it = tmp.begin();
	it++;
	tmp.erase(it);
	set<int> ::iterator it1 = tmp.find(2);
	if (it1 != tmp.end()) 
	{cout << "找到了数据为:" << *it1 << endl;}
	else 
	{cout << "容器中没有这个数据" << endl;}
}

容器一开始装的数据为2,1,5,4,3,it一开始指向的是容器中的第一个元素 1,将it的值加加那么他就会指向容器中的第二个元素也就是2,当我们使用erase函数将迭代器指向的第二个元素删除之后再去查找数据2,便会发现找不到了
在这里插入图片描述
那么这就是erase函数的第一种形式,通过传递迭代器具体指向的位置来删除元素,这种形式的erase主要和find函数互相搭配使用,find函数找到元素之后会通过迭代器去的形式返回元素所在的位置,那么我们就可以通过erase的第一种形式将其删除,比如说下面的代码:

void func3()
{
	vector<int> v = { 2,1,5,4,3 };
	set<int> tmp(v.begin(), v.end());
	set<int>::iterator it = tmp.find(4);
	if (it != tmp.end())
	{
		tmp.erase(it);
	}
	it = tmp.find(4);
	if (it != tmp.end()) 
	{cout << "找到了数据为:" << *it << endl;}
	else 
	{cout << "容器中没有这个数据" << endl;}
}

这段代码的运行结果如下:
在这里插入图片描述
那么这就是erase函数的第一种形式的用法,第二种形式就是值删除比如说我想要删除容器种的3的话我就可以在erase函数种直接添加一个3,如果删除成功的话erase函数就会返回1如果删除的元素不存在导致删除失败的话就会返回0,比如说下面的代码:

void func3()
{
	vector<int> v = { 2,1,5,4,3 };
	set<int> tmp(v.begin(), v.end());
	cout << tmp.erase(3)<<endl;
	cout << tmp.erase(6) << endl;
}

这段代码的运行结果如下:
在这里插入图片描述

第三个就是迭代器区间的删除,这种删除方式就是将迭代器区间的内容给删除掉,当然这里的迭代器指的是set<T>的迭代器,比如说下面的代码:

void func3()
{
	vector<int> v = { 2,1,5,4,3,6,8,7 };
	set<int> tmp(v.begin(), v.end());
	tmp.erase(++tmp.begin(), --tmp.end());
}

这里的erase函数执行完之后容器中只会存在两个元素,一个是第一个元素,另外一个就是最后一个元素
在这里插入图片描述
那么这就是erase函数三种参数形式的使用方法以及特点希望大家能够理解。

count函数

我们来看看cout函数的参数和这个函数的作用:
在这里插入图片描述

cout函数的作用是返回这个值在树中出现的个数,这个函数的作用就是返回这个值在不在树里面,如果不在的话这个函数就会返回0,如果在的话这个函数就会这个数据在容器里面出现的个数,可能大家会感觉这个函数的存在属实有一点点鸡肋,因为find函数也可以干这种事情,那为什么还要这个函数呢?那这里就有两个原因,第一个就是conut函数可以更加方便的判断某个元素在不在,find函数的返回值是一个迭代器我们想要知道在不在的话得拿这个迭代器和容器的end函数做比较,而count函数则可以直接判断,第二个原因就是这个函数主要是为multiset进行服务的这个multiset也是可以看成set容器但是他允许数据冗余,也就是说容器中可以存在多个相同的数据,那么这就是count函数的作用希望大家能够理解。

lower_bound

upper_bound

我们首先来看看这两个函数的参数和介绍:
在这里插入图片描述
在这里插入图片描述

lower_bound和upper_bound的作用就是确定一个区间,lower_bound(x)找的就是大于等于x的位置upper_bound(y)找的就是大于y的位置,比如下面的代码:

void func4()
{
	vector<int> v = { 1,4,3,8,7,10,11,13 ,6};
	set<int> tmp(v.begin(), v.end());
	set<int>::iterator it_begin = tmp.lower_bound(5);
	cout << *it_begin << endl;
	set<int>::iterator it_end = tmp.upper_bound(8);
	cout << *it_end << endl;
}

因为容器中没有5所以lower_bound(5)找到的元素就是6,由于容器中存在8所以upper_bound(8)找的是大于8的元素的位置,所以upper_bound函数找到的就是10,比如说下面的代码:
在这里插入图片描述
这两个函数没什么用,可能唯一有用的地方就是搭配erase函数删除一段区间的元素给删除了,比如说下面的代码:

void func4()
{
	vector<int> v = { 1,4,3,8,7,10,11,13 ,6};
	set<int> tmp(v.begin(), v.end());
	set<int>::iterator it_begin = tmp.lower_bound(5);
	cout << *it_begin << endl;
	set<int>::iterator it_end = tmp.upper_bound(8);
	cout << *it_end << endl;
	tmp.erase(it_begin, it_end);
}

这里就会将大于等于6小于10的元素全部都给删除了,这里通过调试给大家看看erase函数的运行结果:
在这里插入图片描述
大家可以看到7和8都被删除了但是10没有被删除,那么这就是两个函数的作用希望大家可以理解。

multiset

multiset也是一个容器:
在这里插入图片描述

我们说set函数的作用是将一段数据进行排序和去重,当往set容器里面插入多个相同数据时,set容器只会存储一个数据,其他数据都会被过滤掉,但是multiset和set不一样,他不会对重复的数据进行过滤所以在这个容器里面就可以存在多个相同的数据,比如说下面的代码:

void func5()
{
	set<int> tmp1;
	tmp1.insert(1);
	tmp1.insert(1);
	tmp1.insert(1);
	multiset<int> tmp2;
	tmp2.insert(1);
	tmp2.insert(1);
	tmp2.insert(1);
}

通过调试便可以看到下面这两个容器的不同:
在这里插入图片描述
multiset容器和set容器的使用方法上没有什么区别,比如说conut函数这个函数在set中只能返回0或者1因为一个元素在set中也只能出现1次或者0次,但是在multiset中相同的元素会出现多次,所以count函数的返回值也会有多个,比如说下面的代码:

void func5()
{
	set<int> tmp1;
	tmp1.insert(1);
	tmp1.insert(1);
	tmp1.insert(1);
	cout << tmp1.count(1) << endl;
	multiset<int> tmp2;
	tmp2.insert(1);
	tmp2.insert(1);
	tmp2.insert(1);
	cout << tmp2.count(1) << endl;
}

这个代码的运行结果如下:
在这里插入图片描述
那么对于multiset大家可能会存在这么几个疑问,我们知道set的底层是通过搜索二叉树来实现的,对于搜索二叉树比根节点大的值会放到根节点的右边,比根节点小的值则会放到根节点的左边,multiset也是基于搜索二叉树实现的,那出现了相等的值这个值放到根节点的左边还是右边呢?答案是左边或者右边都可以,因为会发生旋转就算你放到左边也可能会因为旋转被放到右边,所以这个时候放到左边或者右边意义就没有那么大了所以multiset的insert函数就只是排序,对于multiset还有个问题就是容器中存在多个相同的数据,那find函数找的是容器中的哪个数据呢?答案是查找到的第一个数据就是find函数返回的,我们可以通过下面的代码来验证这一点:

void func6()
{
	vector<int> v = { 1,1,2,2,2,3,4,5,6 };
	multiset<int> tmp1(v.begin(),v.end());
	multiset<int>:: iterator it=tmp1.find(2);
	while (*it == 2)
	{
		cout << *it << endl;
		++it;
	}
}

这段代码的运行结果如下:
在这里插入图片描述
可以看到这里打印出来了三个2,所以这里的find找到就是第一个出现的数据,那么这就是multiset的用法即性质希望大家能够理解。

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

详解c++---set的介绍 的相关文章

  • 为什么通过派生类对基类的引用与 :: - 运算符不明确?

    所以我想知道为什么以下钻石问题的代码片段无法编译 我知道这个问题通常是通过虚拟继承来解决的 我不是故意使用它的 该代码只是为了展示我的问题 即为什么编译器称此不明确 因此 我在 struct Base 中声明了两个成员变量 因为这两个子类
  • WebClient.DownloadDataAsync 冻结了我的 UI

    我在 Form 构造函数中的 InitializeComponent 之后有以下代码 using WebClient client new WebClient client DownloadDataCompleted new Downloa
  • 如何使用 ASP.NET MVC 编辑多选列表?

    我想编辑一个如下所示的对象 我希望用 UsersGrossList 中的一个或多个用户填充 UsersSelectedList 使用 mvc 中的标准编辑视图 我只得到映射的字符串和布尔值 下面未显示 我在 google 上找到的许多示例都
  • 是否可以从 C++ 应用程序调用 C# 应用程序?

    我是一名编程学生 现在我已经上了两门 C 课程 这个学期我将参加我的第一门 C 课程 出于好奇 是否可以从 C 应用程序调用 C 应用程序 如果是的话 是否还可以检查运行该程序的计算机是否具有 NET框架 我只是很好奇 我想如果可能的话 这
  • c# 从另一个类中的另一个静态事件引发事件

    需要帮助从另一个班级调用事件 我有已声明事件的课程 public class MxPBaseGridView GridView public event AddNewItemsToPopUpMenuEventHandler AddNewIt
  • 异常堆栈跟踪不显示抛出异常的位置

    通常 当我抛出异常 捕获它并打印出堆栈跟踪时 我会看到抛出异常的调用 导致该异常的调用 导致该异常的调用that 依此类推回到整个程序的根 现在它只向我显示异常所在的调用caught 而不是它所在的地方thrown 我不明白是什么改变导致了
  • 如果 JSON.NET 中的值为 null 或空格,则防止序列化

    我有一个对象需要以这样的方式序列化 即 null 和 空白 空或只是空格 值都不会序列化 我不控制对象本身 因此无法设置属性 但我知道所有属性都是字符串 环境NullValueHandling显然 忽略 只能让我找到解决方案的一部分 它 似
  • C# 处理标准输入

    我目前正在尝试通过命令行断开与网络文件夹的连接 并使用以下代码 System Diagnostics Process process2 new System Diagnostics Process System Diagnostics Pr
  • C++ 将联合强制转换为其成员类型之一

    以下对我来说似乎完全符合逻辑 但不是有效的 C 联合不能隐式转换为其成员类型之一 有人知道为什么不这样做的充分理由吗 union u int i char c function f int i int main u v v i 6 f v
  • C# 编译器数字文字

    有谁知道 C 编译器数字文字修饰符的完整列表 默认情况下 声明 0 使其成为 Int32 声明 0 0 使其成为 Double 我可以在末尾使用文字修饰符 f 来确保某些内容被视为 Single 例如像这样 var x 0 x is Int
  • 在 C++11 中移出 stdpriority_queue 的元素

    最小的工作示例 include
  • 在 C# 中何时使用 ArrayList 而不是 array[]?

    我经常使用一个ArrayList而不是 正常 array 当我使用时 我感觉好像我在作弊 或懒惰 ArrayList 什么时候可以使用ArrayList在数组上 数组是强类型的 并且可以很好地用作参数 如果您知道集合的长度并且它是固定的 则
  • C# 中的 C/C++ 代码编译器

    在 C 中 我可以使用下面的代码编译 VB 和 C 代码 但无法编译 C C 代码 有什么办法可以做到这一点吗 C 编译器 public void Compile string ToCompile string Result null st
  • 浮点字节序?

    我正在为实时海上模拟器编写客户端和服务器 并且由于我必须通过套接字发送大量数据 因此我使用二进制数据来最大化可以发送的数据量 我已经了解整数字节顺序以及如何使用htonl and ntohl为了规避字节顺序问题 但我的应用程序与几乎所有模拟
  • Autoconf 问题:“错误:C 编译器无法创建可执行文件”

    我正在尝试使用 GNU 自动工具构建一个用 C 编写的程序 但显然我设置错误 因为当configure运行 它吐出 configure error C compiler cannot create executables 如果我看进去con
  • Linq.Select() 中的嵌套表达式方法调用

    I use Select i gt new T 每次手动点击数据库后将我的实体对象转换为 DTO 对象 以下是一些示例实体和 DTOS 用户实体 public partial class User public int Id get set
  • 如果“嵌入式”SQL 2008 数据库文件不存在,如何创建它?

    我使用 C ADO Net 和在 Server Management Studio 中创建的嵌入式 MS SQL 2008 数据库文件 附加到 MS SQL 2008 Express 创建了一个数据库应用程序 有人可以向我指出一个资源 该资
  • 将 char 绑定到枚举类型

    我有一段与此非常相似的代码 class someclass public enum Section START MID END vector section Full void ex for int i 0 i section
  • 如何在c linux中收听特定接口上的广播?

    我目前可以通过执行以下操作来收听我编写的简单广播服务器 仅广播 hello int fd socket PF INET SOCK DGRAM 0 struct sockaddr in addr memset addr 0 sizeof ad
  • 嵌入式二进制资源 - 如何枚举嵌入的图像文件?

    我按照中的说明进行操作这本书 http www apress com book view 9781430225492 关于资源等的章节 我不太明白的是 如何替换它 images Add new BitmapImage new Uri Ima

随机推荐

  • mac ping: sendto: Host is down

    mac ping 内网机子提示 host is down Request timeout for icmp seq 0 但是其他小伙伴ping是没问题的 mac和小伙伴的电脑网段 子网掩码 路由器 DNS一致 查询后是因为mac使用了vmw
  • 手机微信连不上wifi服务器怎么回事,微信连不上wifi怎么办?

    大家经常会在家中使用微信进行聊天 那么如果微信连不上wifi了怎么办 方法步骤 1 微信是大家最常用的聊天工具之一了 几乎每天都在使用 大家在家里使用的话经常会连接wifi 但有时候会遇到微信连不上wifi的问题 却又不知道怎么解决 接下来
  • 全角字符unicode码对应表

    Uni GB Uni GB Uni GB Uni GB Uni GB 00A4 A1E8 00A7 A1EC 00A8 A1A7 00B0 A1E3 00B1 A1C0 00B7 A1A4 00D7 A1C1 00E0 A8A4 00E1
  • 对于Transformer 模型----可以从哪些地方进行创新和改进

    Vit 全称 Vision Transformer 是Transformer在CV方向的应用 是NLP与CV的相互联系 相互促进 相互影响 自Transformer应用进计算机视觉领域以来 与其相结合的新模型大都表现出了不错的效果 但是 这
  • 微信小程序:排行榜页面模板

    文章目录 1 前言 2 模板代码 3 结语 1 前言 在开发一款背单词的微信小程序时 为了加强用户的体验感 刺激用户积极学习 小程序中需要有排行榜的模块 通过打卡天数来排名 让用户有攀比学习的心里 具体的页面截图如下 2 模板代码 wxml
  • python-数据分析(6-numpy)

    Numpy 6 Numpy 6 1 Numpy介绍与安装 Numpy是什么 Numpy Numerical Python 是目前Python数值计算中最为重要的基础包 大多数计算包都提供了基于Numpy的科学函数功能 将Numpy的数组对象
  • C#开发系列(四)——文档注释

    C 为程序员提供一种机制 以使用包含 XML 文本的特殊注释语法记录其代码 在源代码文件中 具有特定窗体的注释可用于指示工具从这些注释生成 XML 并将其置于后面 使用此语法的注释称为文档注释 它们必须紧跟在用户定义的类型 如类 委托或接口
  • EF Core 迁移数据库,以及对数据库升级的思考

    这两天一直在学习ABP VNext框架 整到数据库那一块了 发现问了问组里大佬 要使用EFCore迁移数据库 我寻思这和我自己以前搞得不太一样 以前是要写SQL或者直接GUI建表 现在怎么命令行敲一下就自动生成了 写个博客记录一下 EF C
  • jvm系列(3)java类加载机制

    我们知道 我们写的java文件是不能直接运行的 我们可以在IDEA中右键文件名点击运行 这中间其实掺杂了一系列的复杂处理过程 这篇文章 我们只讨论我们的代码在运行之前的一个环节 叫做类的加载 按照我写文章的常规惯例 先给出这篇文章的大致结构
  • 阿里三面 失败告终

    update 2015 04 16 在一个tomcat下 用classloader加载了某个类之后会将该类信息放入方法区 永久代 当这个类创建了某个线程 比如周期显示当前时间 那么会导致这个类信息一直存在于永久区中 即使这个类的主要工作已经
  • mysql集群+复制

    详解MySQL集群下的复制 replicate 原理 1 集群下的复制 1 1 简述 从MySQL 5 1 开始 就支持集群 复制了 这对于想要构建一个高可用方案的用户来说 无疑是个惊喜 在这种模式下 既有主从的实时备份 又有基于集群的负载
  • 《算法导论》常见算法总结

    前言 本篇文章总结中用到很多其他博客内容 本来想附上原作链接 但很久了未找到 这里关于原创性均来源于原作者 分治法 分治策略的思想 顾名思义 分治是将一个原始问题分解成多个子问题 而子问题的形式和原问题一样 只是规模更小而已 通过子问题的求
  • 大数定理与中心极限定理

    大数定律 定义 理解 可以用样本均值估计总体分布的均值 频率趋近于概率 举例 抛N次硬币 当N趋近于无穷大时 正面出现的频率等于正面出现的概率 中心极限定理 定义 林德贝格 勒维中心极限定理 理解 1 样本的平均值约等于总体的平均值 2 不
  • 解决php中redis client进行subscribe操作出现timeout的问题

    出现该问题的原因是poll设置接收超时所致 这个超时默认设置60s 设置Redis OPT READ TIMEOUT配置项 解决方法如下
  • python串口模块_使用python pyserial模块串口通信

    最近调试通信模块时 需要用UART串口输入AT命令控制模块 手动输入不便于自动化 所以就学习了下使用python进行串口控制 serial模块安装 pip install pyserial 常用的方法函数 导入串口模块import seri
  • SpringBoot过滤器Filter的使用-基础篇

    1 过滤器 Filter 简介 1 1 过滤器 Filter 介绍 Filter 是 JavaEE 中 Servlet 规范的一个组件 位于包javax servlet 中 它可以在 HTTP 请求到达 Servlet 之前 被一个或多个F
  • 目的:VSCode Remote-SSH连接远程失败timeout

    目的 VSCode Remote SSH连接远程失败timeout 环境 系统 win10 环境 VSCode 1 51 1 问题分析 正常使用VSCode的情况下 突然发现 解决步骤 判断可能是ssh问题 cmd打开控制台或者进入wind
  • 【华为OD统一考试A卷

    华为OD统一考试A卷 B卷 新题库说明 2023年5月份 华为官方已经将的 2022 0223Q 1 2 3 4 统一修改为OD统一考试 A卷 和OD统一考试 B卷 你收到的链接上面会标注A卷还是B卷 请注意 根据反馈 目前大部分收到的都是
  • 网络工程专业毕业设计选题汇总

    文章目录 0 简介 1 如何选题 2 最新网络工程选题 2 1 Java web SSM 系统 2 2 大数据方向 2 3 人工智能方向 2 4 其他方向 4 最后 0 简介 学长搜集分享最新的网络工程专业毕设毕设选题 难度适中 适合作为毕
  • 详解c++---set的介绍

    目录标题 set容器的介绍 set的构造函数 insert函数的介绍 find函数 erase函数 count函数 lower bound upper bound multiset set容器的介绍 set容器可以看成我们上一篇文章学习的K