【C++】—— set 与 multiset
【C++】—— map 与 set
- 1 序列式容器和关联式容器
- 2 set 系列的使用
- 2.1 set 和 multiset 参考文档
- 2.2 set 类的介绍
- 2.3 set 的迭代器和构造
- 2.4 set的增删查
- 2.4.1 insert
- 2.4.2 find 与 erase
- 2.4.3 count
- 2.5 lower_bound 与 upper_bound
- 2.6 multiset 与 set 的差异
- 2.6.1 不再去重
- 2.6.2 find 返回中序的第一个
- 2.6.3 erase 删除所有的 x
- 2.6.4 count 个数
1 序列式容器和关联式容器
前面我们已经接触过 STL 中的部分容器如:string、vector、list、deque、array等,这些容器统称为序列式容器,因为他们是逻辑结构为线性序列的数据结构(物理结构不一定为线性),两个位置存储的值之间一般没有紧密的关联关系,比如交换一下,它依旧是序列式容器。顺序容器中的元素是按他们在容器中的存储位置来顺序保存和访问的
关联式容器也是用来存储数据的,与序列式容器不同的是,关联式容器逻辑结构通常是非线性结构,两个位置有紧密的关联关系,交换一下,他的存储结构就被破坏了。关联式容器中的元素是按关键字来保存和访问的。关联式容器有 map / set 系列和 unordered_map / unordered_set 系列
本文讲解的 m a p map map 和 s e t set set 底层是红黑树,红黑树是一颗平衡二叉搜索树。 s e t set set 是 k e y key key 搜索场景的结构, m a p map map 是 k e y key key / v a l u e value value 搜索场景的结构
2 set 系列的使用
2.1 set 和 multiset 参考文档
https://legacy.cplusplus.com/reference/set/
2.2 set 类的介绍
set 的声明如下:
template < class T, // set::key_type/value_typeclass Compare = less<T>, // set::key_compare/value_compareclass Alloc = allocator<T> // set::allocator_type> class set;
• s e t set set 的声明如上,T 就是 s e t set set 底层关键字的类型
• s e t set set 默认要求 T 支持小于比较,如果不支持或者想按自己的需求走可以自行实现仿函数传给第⼆个模版参数
• s e t set set 底层存储数据的内存是从空间配置器申请的,如果需要可以自己实现内存池,传给第三个参数。
• ⼀般情况下,我们都不需要传后两个模版参数。
• s e t set set 底层是用红黑树实现,增删查效率是 O(logN),迭代器遍历是⾛的搜索树的中序,所以是有序的。
• 前面部分我们已经学习了 v e c t o r vector vector / l i s t list list 等容器的使用,STL 容器接口设计,高度相似,所以这里我们就不再⼀个接口⼀个接口的介绍,而是直接带着大家看文档,挑比较重要的接口进行介绍。
这里库中的模板参数用的是 T,个人认为这里设计的不够好,既然是搜索,那用 Key 更好,我们可以将其当成是 Key,虽然库中没用这个名字。
2.3 set 的迭代器和构造
s e t set set 的迭代器是一个双向迭代器:
// 正向迭代器
iterator begin();
iterator end();
// 反向迭代器
reverse_iterator rbegin();
reverse_iterator rend();
s e t set set 主要构造方式如下:
- 无参构造
explicit set(const key_compare& comp = key_compare(),const allocator_type& alloc = allocator_type());
- 迭代器区间构造
template <class InputIterator>
set(InputIterator first, InputIterator last,const key_compare& comp = key_compare(),const allocator_type& alloc = allocator_type());
- 拷贝构造
set(const set& x);
set(const set& x, const allocator_type& alloc);
- 列表构造
set(initializer_list<value_type> il,const key_compare& comp = key_compare(),const allocator_type& alloc = allocator_type());
2.4 set的增删查
首先要注意的是: s e t set set 是不支持改的,因为 s e t set set 属于关联式容器,对齐进行数据修改会破坏它的存储结构
2.4.1 insert
// 单个数据插⼊,如果已经存在则插⼊失败
pair<iterator, bool> insert(const value_type& val);// 列表插⼊,已经在容器中存在的值不会插⼊
void insert(initializer_list<value_type> il);// 迭代器区间插⼊,已经在容器中存在的值不会插⼊
template <class InputIterator>
void insert(InputIterator first, InputIterator last);
这里 v a l u e value value_ t y p e type type 就是 T,即 Key;此外 k e y key key_ t y p e type type 也是 T。这里这么设计是为了和后面的 m a p map map 保持一致
返回值 pair<iterator, bool> 这里还不需要用到它,暂不解释,在后面的 m a p map map 部分会详细介绍。
举个栗子:
int main()
{// 去重+升序排序set<int> s;// 去重+降序排序(给⼀个⼤于的仿函数)//set<int, greater<int>> s;s.insert(5);s.insert(2);s.insert(7);s.insert(5);set<int>::iterator it = s.begin();while (it != s.end()){// error C3892: “it”: 不能给常量赋值// *it = 1;cout << *it << " ";++it;}cout << endl;。return 0;
}
运行结果:
可以看到, s e t set set 是不允许两个相同的值进行插入的,即 s e t set set 有去重功能。并且 s e t set set 是不允许修改的,*it = 1 进行修改会报错。
默认 s e t set set 走的是升序,如果想走降序,可以传递一个大于的仿函数。迭代器的底层走的是一个中序遍历
s e t set set 支持列表插入
int main()
{set<int> s;s.insert({ 2,8,3,9,2 });for (auto e : s){cout << e << " ";} cout << endl;return 0;
}
s e t set set 除了支持整型,其他任意类型都是可以的(如果该类型不支持比较,需自己传递仿函数),我们以 s t r i n g string string 为例
int main()
{// void insert(initializer_list<value_type> il);// 构造出临时对象再拷贝构造,优化成直接构造set<string> strset = { "sort", "insert", "add" };// 遍历string⽐较ascll码⼤⼩顺序遍历的for (auto& e : strset){cout << e << " ";} return 0;
}
2.4.2 find 与 erase
const_iterator find(const value_type& val) const;
iterator find(const value_type& val);
f i n d find find 返回的是对应位置的迭代器,如果没找到就返回 end()
- 迭代器删除
iterator erase(const_iterator position);
- Key删除
size_type erase(const value_type& val);
s i z e size size_ t y p e type type 是一个无符号整型,即 u n s i g n e d unsigned unsigned i n t int int,返回的是删除元素的个数。
如果删除成功,表示删除了一个值,返回 1;删除失败,表示没有删除值,返回 0
为什么这里不用 b o o l bool bool 呢?这里是为了兼容 m u l t i s e t multiset multiset。
m u l t i s e t multiset multiset 中允许相同数插入的,可能 e r a s e erase erase 多个相同的值,这时就不能用 b o o l bool bool 了
- 迭代器区间删除
iterator erase(const_iterator first, const_iterator last);
栗子:
int main()
{set<int> s = { 4,2,7,2,8,5,9 };for (auto e : s){cout << e << " ";} cout << endl;// 删除最⼩值s.erase(s.begin());for (auto e : s){cout << e << " ";}cout << endl;// 直接删除xint x;cin >> x;int num = s.erase(x);if (num == 0){cout << x << "不存在!" << endl;} for (auto e : s){cout << e << " ";}cout << endl;// 直接查找在利⽤迭代器删除xcin >> x;auto pos = s.find(x);if (pos != s.end()){s.erase(pos);}else{cout << x << "不存在!" << endl;}for (auto e : s){cout << e << " ";} cout << endl;return 0;
}
s e t set set 进行删除同样会导致 迭代器失效
i )
此时删除的是叶子结点 1,删除后迭代器中的指针为野指针,迭代器失效。
ii )
删除 6 节点。它要与 5 或 7 节点进行交换,删除进行删除。虽然此时迭代器中的指针并不是野指针,但它原来的意义已经改变了,我们也认为是迭代器失效。
而且从我们的角度,我们不知道这个节点是直接删除还是替代法删除。
p s ps ps:对二叉搜索树的删除有不理解的小伙伴可移步至:【C++】—— 二叉搜索树
2.4.3 count
c o u n t count count 的功能是返回 v a l u e value value_ t y p e type type 在容器中的个数
size_type count (const value_type& val) const;
c o u n t count count 是给 m u l t i s t multist multist 设计的,因为对 s e t set set 而言 c o u n t count count 要么返回 1 要么返回 0,并没有什么意义。但对于 m u l t i s e t multiset multiset 就不一样, m u l t i s e t multiset multiset 是允许多个相同值存在的
我们可以用 c o u n t count count 来判断一个 K e y Key Key 在不在,在的话返回的是 1,不在返回 0。
而且用 c o u n t count count 来判断往往比 f i n d find find 更方便,因为 f i n d find find 返回的是迭代器,迭代器 != end() 才是找到了
int main()
{set<int> s = { 4,2,7,2,8,5,9 };// 利⽤count间接实现快速查找int x;cin >> x;if (s.count(x)){cout << x << "在!" << endl;} else{cout << x << "不存在!" << endl;} return 0;
}
2.5 lower_bound 与 upper_bound
// 返回⼤于等于val位置的迭代器
iterator lower_bound(const value_type& val) const;
// 返回⼤于val位置的迭代器
iterator upper_bound(const value_type& val) const;
l o w e r lower lower_ b o u n d bound bound 与 u p p e r upper upper_ b o u n d bound bound 是为了方便查找一段区间
我们曾经说过,在 STL 里面,只要是迭代器区间必须是 左闭右开,这时就可以用到 l o w e r lower lower_ b o u n d bound bound 与 u p p e r upper upper_ b o u n d bound bound
举个栗子:
int main()
{std::set<int> myset;for (int i = 1; i < 10; i++)myset.insert(i * 10); // 10 20 30 40 50 60 70 80 90for (auto e : myset){cout << e << " ";}cout << endl; 实现查找到的[itlow,itup)包含[30, 60]区间 返回 >= 30(这里即返回30)//auto itlow = myset.lower_bound(30); 返回 > 60(这里即返回70)//auto itup = myset.upper_bound(60);// 实现查找到的[itlow,itup)包含[25, 55]区间//对应别人来说,并不知道容器里面是否有 25 和 55,但是查找这段区间的方法与查找30-60的方法一样的// 返回 >= 25(这里即返回30)auto itlow = myset.lower_bound(25);// 返回 > 55(这里即返回60)auto itup = myset.upper_bound(55);// 删除这段区间的值myset.erase(itlow, itup);for (auto e : myset){cout << e << " ";}cout << endl;return 0;
}
2.6 multiset 与 set 的差异
m u l t i s e t multiset multiset 和 s e t set set 的使用基本完全类似,主要区别点在于 m u l t i s e t multiset multiset 支持值冗余,那么 i n s e r t insert insert / f i n d find find / c o u n t count count / e r a s e erase erase 都围绕着支持值冗余有所差异,具体参看下面的样例代码理解。
2.6.1 不再去重
- 相比 s e t set set 不同的是, m u l t i s e t multiset multiset 是排序,但是不去重
int main()
{multiset<int> s = { 4,2,7,2,4,8,4,5,4,9 };auto it = s.begin();while (it != s.end()){cout << *it << " ";++it;}cout << endl;return 0;
}
2.6.2 find 返回中序的第一个
- 相比 s e t set set 不同的是, x x x 可能会存在多个, f i n d find find 查找中序遍历的第一个
int main()
{multiset<int> s = { 4,2,7,2,4,8,4,5,4,9 };int x;cin >> x;auto pos = s.find(x);while (pos != s.end() && *pos == x){cout << *pos << " ";++pos;} cout << endl;return 0;
}
简单讲一下他是怎么找到中序的第一个的。
查找依然是 O(logN) 算法的查找,并不是通过中序的方式遍历一遍,这样的话二叉搜索树就失去其意义。
中序的查找规则是:左子树 -> 根 -> 右子树。
假设要找的值是 5 :如果找到了一个 5 ,就再往其左子树找,因为中序第一个 5 一定是在左树;直到找到了某一个 5 ,并且其左子树没有 5 ,表面当前 5 就是中序的第一个 5。
为什么这里要找中序的第一个呢? 因为找中序的第一个 x,就可以用迭代器不断++,就可以找到所有的 x
2.6.3 erase 删除所有的 x
- 相比 s e t set set 不同的是, e r a s e erase erase 给值时会
删除所有的 x
int main()
{multiset<int> s = { 4,2,7,2,4,8,4,5,4,9 };for (auto e : s){cout << e << " ";} cout << endl;int x;cin >> x;s.erase(x);for (auto e : s){cout << e << " ";} cout << endl;return 0;
}
2.6.4 count 个数
- 相比 s e t set set 不同的是, c o u n t count count 会返回 x x x 的实际个数
int main()
{multiset<int> s = { 4,2,7,2,4,8,4,5,4,9 };for (auto e : s){cout << e << " ";}cout << endl;int x;cin >> x;cout << s.count(x) << endl;return 0;
}
好啦,本期关于 s e t set set 与 m u l t i s e t multiset multiset 的知识就介绍到这里啦,希望本期博客能对你有所帮助。同时,如果有错误的地方请多多指正,让我们在 C++ 的学习路上一起进步!
相关文章:
【C++】—— set 与 multiset
【C】—— map 与 set 1 序列式容器和关联式容器2 set 系列的使用2.1 set 和 multiset 参考文档2.2 set 类的介绍2.3 set 的迭代器和构造2.4 set的增删查2.4.1 insert2.4.2 find 与 erase2.4.3 count 2.5 lower_bound 与 upper_bound2.6 multiset 与 set 的差异2.6.1 不再去重2…...
蓝桥杯-扫雷
这题不难,就是麻烦一点,这里暴力求解了直接 题目链接: 扫雷 AC代码: import java.util.Scanner; // 1:无需package // 2: 类名必须Main, 不可修改public class Main {public static void main(String[] args) {Scanner scan ne…...
黑马JavaWeb-day06、07、08(SQL部分) _
文章目录 MYSQL概述数据模型SQL简介SQL分类 DDL数据库操作表操作 DML增(INSERT)改(UPDATE)删(DELETE) DQL基本查询条件查询(where)分组查询(group by)排序查询…...
三十五:Wireshark的捕获过滤器
Wireshark 是一个广泛使用的网络协议分析工具,主要用于捕获和分析网络流量。它支持丰富的协议分析,并提供了多种过滤方式,以便用户在大量数据中精确地找到自己关注的内容。在Wireshark中,过滤器可以分为两类:捕获过滤器…...
第9章 大模型的有害性(上)
9.1 引言 本章将探讨大型语言模型(LLMs)可能带来的有害性,重点讨论以下几个方面: 性能差异社会偏见和刻板印象 在后续内容中,还会涉及其他层面的危害,如有害信息、虚假信息、隐私和安全风险、版权问题、…...
遗传算法与深度学习实战(26)——编码卷积神经网络架构
遗传算法与深度学习实战(26)——编码卷积神经网络架构 0. 前言1. EvoCNN 原理1.1 工作原理1.2 基因编码 2. 编码卷积神经网络架构小结系列链接 0. 前言 我们已经学习了如何构建卷积神经网络 (Convolutional Neural Network, CNN),在本节中&a…...
Linux无线网络配置工具:iwconfig vs iw
在Linux系统中,无线网络配置和管理是网络管理员和开发者的常见任务。本文将详细介绍两个常用的无线网络配置命令行工具:iwconfig 和 iw,并对比它们之间的区别,帮助您更好地选择合适的工具进行无线网络配置。 一、iwconfig 简介 …...
RabbitMQ介绍及安装
文章目录 一. MQ二. RabbitMQ三. RabbitMQ作用四. MQ产品对比五. 安装RabbitMQ1. 安装erlang2. 安装rabbitMQ3. 安装RabbitMQ管理界⾯4. 启动服务5. 访问界面6. 添加管理员用户7. 重新登录 一. MQ MQ( Message queue ), 从字⾯意思上看, 本质是个队列, FIFO 先⼊先出ÿ…...
借助 AI 工具,共享旅游-卡-项目助力年底增收攻略
年底了,大量的商家都在开始筹备搞活动,接下来的双十二、元旦、春节、开门红、寒假,各种活动,目的就是为了拉动新客户。 距离过年还有56 天,如何破局? 1、销售渠道 针对旅游卡项目,主要销售渠道…...
Docker Compose 和 Kubernetes 之间的区别?
一、简介🎀 1.1 Docker Compose Docker Compose 是 Docker 官方的开源项目,负责实现对 Docker 容器集群的快速编排,可以管理多个 Docker 容器组成一个应用。你只需定义一个 YAML 格式的配置文件 docker-compose.yml ,即可创建并…...
node.js常用的模块和中间件?
Node.js常用的模块和中间件包括以下几种: Express:Express是一个灵活的Node.js web应用框架,提供了丰富的API来处理HTTP请求和响应。它支持中间件系统,可以轻松地添加各种功能,如路由、模板引擎、静态文件服务…...
Llama模型分布式训练(微调)
1 常见大模型 1.1 参数量对照表 模型参数量发布时间训练的显存需求VGG-19143.68M2014~5 GB(单 224x224 图像,batch_size32)ResNet-15260.19M2015~7 GB(单 224x224 图像,batch_size32)GPT-2 117M117M2019~…...
Matlab模块From Workspace使用数据类型说明
Matlab原文连接:Load Data Using the From Workspace Block 模型: 从信号来源的数据: timeseries 数据: sampleTime 0.01; numSteps 1001;time sampleTime*[0:(numSteps-1)]; time time;data sin(2*pi/3*time);simin time…...
LangChain学习笔记(一)-LangChain简介
LangChain学习笔记(一)-LangChain简介 langChain是一个人工智能大语言模型的开发框架,主要构成为下图。 一、核心模块 (一)模型I/O模块 负责与现有大模型进行交互,由三部分组成: 提…...
k8s,声明式API对象理解
命令式API 比如: 先kubectl create,再replace的操作,我们称为命令式配置文件操作 kubectl replace的执行过程,是使用新的YAML文件中的API对象,替换原有的API对象;而kubectl apply,则是执行了一…...
KubeBlocks v0.9.2发布啦!支持容器镜像滚动更新、MySQL支持Jemalloc...快来升级体验更多新功能!
KubeBlocks v0.9.2 正式发布啦!本次发布包含了一些新功能、关键的错误修复以及各种改进。以下是详细的更新内容。 升级文档 v0.9.2 升级方式与 v0.9.1 相同,替换版本即可哦~ https://kubeblocks.io/docs/release-0.9/user_docs/upgrade/up…...
Linux-虚拟环境
文章目录 一. 虚拟机二. 虚拟化软件三. VMware WorkStation四. 安装CentOS操作系统五. 在VMware中导入CentOS虚拟机六. 远程连接Linux系统1. Finalshell安装2. 虚拟机网络配置3. 连接到Linux系统 七. 虚拟机快照 一. 虚拟机 借助虚拟化技术,我们可以在系统中&#…...
window系统下的git怎么在黑窗口配置代理
在Windows系统下,通过黑窗口(命令行界面)配置Git代理主要有两种方式:配置HTTP代理和配置SOCKS5代理。以下是具体的步骤: 配置HTTP代理 临时代理设置(仅对当前命令行会话有效): set …...
网络和通信详解
一、Java 网络编程基础 IP 地址和端口号 IP 地址: IP 地址是互联网协议地址,用于标识网络中的设备。在 Java 中,InetAddress类是用于表示 IP 地址的主要类。例如,InetAddress.getByName("www.example.com")可以获取指定…...
网络安全框架及模型-PPDR模型
网络安全框架及模型-PPDR模型 概述: 为了有效应对不断变化的网络安全环境,人们意识到需要一种综合性的方法来管理和保护网络安全。因此,PPDR模型应运而生。它将策略、防护、检测和响应四个要素结合起来,提供了一个全面的框架来处理网络安全问题。 工作原理: PPDR模型的…...
【Axure高保真原型】引导弹窗
今天和大家中分享引导弹窗的原型模板,载入页面后,会显示引导弹窗,适用于引导用户使用页面,点击完成后,会显示下一个引导弹窗,直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...
【HTML-16】深入理解HTML中的块元素与行内元素
HTML元素根据其显示特性可以分为两大类:块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...
IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)
文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...
Mysql8 忘记密码重置,以及问题解决
1.使用免密登录 找到配置MySQL文件,我的文件路径是/etc/mysql/my.cnf,有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...
省略号和可变参数模板
本文主要介绍如何展开可变参数的参数包 1.C语言的va_list展开可变参数 #include <iostream> #include <cstdarg>void printNumbers(int count, ...) {// 声明va_list类型的变量va_list args;// 使用va_start将可变参数写入变量argsva_start(args, count);for (in…...
Golang——7、包与接口详解
包与接口详解 1、Golang包详解1.1、Golang中包的定义和介绍1.2、Golang包管理工具go mod1.3、Golang中自定义包1.4、Golang中使用第三包1.5、init函数 2、接口详解2.1、接口的定义2.2、空接口2.3、类型断言2.4、结构体值接收者和指针接收者实现接口的区别2.5、一个结构体实现多…...
【p2p、分布式,区块链笔记 MESH】Bluetooth蓝牙通信 BLE Mesh协议的拓扑结构 定向转发机制
目录 节点的功能承载层(GATT/Adv)局限性: 拓扑关系定向转发机制定向转发意义 CG 节点的功能 节点的功能由节点支持的特性和功能决定。所有节点都能够发送和接收网格消息。节点还可以选择支持一个或多个附加功能,如 Configuration …...
起重机起升机构的安全装置有哪些?
起重机起升机构的安全装置是保障吊装作业安全的关键部件,主要用于防止超载、失控、断绳等危险情况。以下是常见的安全装置及其功能和原理: 一、超载保护装置(核心安全装置) 1. 起重量限制器 功能:实时监测起升载荷&a…...
计算机系统结构复习-名词解释2
1.定向:在某条指令产生计算结果之前,其他指令并不真正立即需要该计算结果,如果能够将该计算结果从其产生的地方直接送到其他指令中需要它的地方,那么就可以避免停顿。 2.多级存储层次:由若干个采用不同实现技术的存储…...
Element-Plus:popconfirm与tooltip一起使用不生效?
你们好,我是金金金。 场景 我正在使用Element-plus组件库当中的el-popconfirm和el-tooltip,产品要求是两个需要结合一起使用,也就是鼠标悬浮上去有提示文字,并且点击之后需要出现气泡确认框 代码 <el-popconfirm title"是…...




