【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 中的部分容器如:stringvectorlistdequearray等,这些容器统称为序列式容器,因为他们是逻辑结构为线性序列的数据结构(物理结构不一定为线性),两个位置存储的值之间一般没有紧密的关联关系,比如交换一下,它依旧是序列式容器。顺序容器中的元素是按他们在容器中的存储位置来顺序保存和访问的

  关联式容器也是用来存储数据的,与序列式容器不同的是,关联式容器逻辑结构通常是非线性结构,两个位置有紧密的关联关系,交换一下,他的存储结构就被破坏了。关联式容器中的元素是按关键字来保存和访问的。关联式容器有 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++ 的学习路上一起进步!

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

华为、华三交换机纯Web下如何创关键VLANIF、操作STP参数

华为交换机WEB操作 使用的是真机S5735&#xff0c;目前主流的版本都适用&#xff08;V1R5~V2R1的就不在列了&#xff0c;版本太老了&#xff0c;界面完全不一样&#xff0c;这里调试线接的console口&#xff0c;电脑的网络接在ETH口&#xff09; 「模拟器、工具合集」复制整段内…

学习threejs,使用canvas更新纹理

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️Texture 贴图 二、&#x1…

Redis设计与实现读书笔记

Redis设计与实现读书笔记 Redis设计与实现[^1]简单动态字符串SDS的基础定义与C字符串的差别常数获取长度杜绝缓冲区溢出减少修改字符串时带来的内存重分配次数二进制安全函数兼容 链表链表和链表节点的实现 字典字典的实现哈希表定义哈希表节点定义字典定义 哈希算法解决键冲突…

【笔记】离散数学 1-3 章

1. 数理逻辑 1.1 命题逻辑的基本概念 1.1.1 命题的概念 命题&#xff08;Proposition&#xff09;&#xff1a;是一个陈述句&#xff0c;它要么是真的&#xff08;true&#xff09;&#xff0c;要么是假的&#xff08;false&#xff09;&#xff0c;但不能同时为真和假。例如…

SQL SERVER 2016 AlwaysOn 无域集群+负载均衡搭建与简测

之前和很多群友聊天发现对2016的无域和负载均衡满心期待&#xff0c;毕竟可以简单搭建而且可以不适用第三方负载均衡器&#xff0c;SQL自己可以负载了。windows2016已经可以下载使用了&#xff0c;那么这回终于可以揭开令人憧憬向往的AlwaysOn2016 负载均衡集群的神秘面纱了。 …

浅谈——Linux命令入门之前奏

目录 一、备份操作系统 1、快照 2、克隆 二、操作系统的使用注意 1、Linux严格区分大小写 2、Linux 文件“扩展名” 3、Linux 中所有的内容以文件的形式进行保存 4、Linux 中所有的存储设备都必须挂载之后才能使用 5、Linux 系统文件目录的结构 6、Linux 系统文件的目…

matlab中disp,fprintf,sprintf,display,dlmwrite输出函数之间的区别

下面是他们之间的区别&#xff1a; disp函数与fprintf函数的区别 输出格式的灵活性 disp函数&#xff1a;输出格式相对固定。它会自动将变量以一种比较直接的方式显示出来。对于数组&#xff0c;会按照行列形式展示&#xff1b;对于字符串&#xff0c;直接原样输出并换行。例如…

计算机视觉——相机标定(Camera Calibration)

文章目录 1. 简介2. 原理3. 相机模型3.1 四大坐标系3.2 坐标系间的转换关系3.2.1 世界坐标系到相机坐标系3.2.2 相机坐标系到图像坐标系3.2.3 像素坐标系转换为图像坐标系3.2.4 世界坐标转换为像素坐标 3.3 畸变3.3.1 畸变类型3.3.1.1 径向畸变&#xff08;Radial Distortion&a…

纯粹直播 1.7.7 |手机版和TV版,聚合六大直播平台,原画播放

纯粹直播是一款开源的应用程序&#xff0c;支持兴趣化主题的游戏直播、户外直播和才艺直播节目。目前可以观看斗鱼、B站、虎牙和抖音等六大直播平台的内容。该应用适配了安卓手机和电视盒子平台使用&#xff0c;并且软件无广告&#xff0c;提供原画质播放体验。 大小&#xff…

【论文复现】隐式神经网络实现低光照图像增强

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; ❀ 隐式神经网络实现低光照图像增强 引言那么目前低光照图像增强还面临哪些挑战呢&#xff1f; 挑战1. 不可预测的亮度降低和噪声挑战2.度量友好…

算法第一弹-----双指针

目录 1.移动零 2.复写零 3.快乐数 4.盛水最多的容器 5.有效三角形的个数 6.查找总价值为目标值的两个商品 7.三数之和 8.四数之和 双指针通常是指在解决问题时&#xff0c;同时使用两个指针&#xff08;变量&#xff0c;常用来指向数组、链表等数据结构中的元素位置&am…

JAVA-平台模块系统原理

菜鸟为了巩固所写 目录 菜鸟为了巩固所写 代码之间的依赖性 绘制类型依赖图 扩展到包之间的依赖关系 进一步延伸到jar包之间的依赖性 组件依赖图 JAVA技术领域中的两个著名的“擦除” Java类型的“大泥球” JAVA模块解析 模块解析的过程 模块路径明确模块的搜索与…

Keil5配色方案修改为类似VSCode配色

1. 为什么修改Keil5配色方案 视觉习惯&#xff1a;如果你已经习惯了VSCode的配色方案&#xff0c;尤其是在使用ESP-IDF开发ESP32时&#xff0c;Keil5的默认配色可能会让你感到不习惯。减少视觉疲劳&#xff1a;Keil5的默认背景可能过于明亮&#xff0c;长时间使用可能会导致视…

微服务监控prometheus+Grafana

目录 Prometheus 概述 核心组件 特点 使用场景 Grafana 概述 功能特点 使用场景 PrometheusGrafana组合 部署和配置 一、准备工作 二、部署Prometheus 三、部署Grafana 四、创建监控仪表盘 五、验证和调优 总结 微服务监控是确保微服务架构稳定运行的关键环节…

⭐Java---反射--获取类信息⭐

目录 三种获取类信息的方式&#xff1a; 一个输入类名字获取类信息的类&#xff1a; 一个测试类&#xff1a; 测试结果 三种获取类信息的方式&#xff1a; 对象.getClass()类.classClass.forname("类的路径") People p; Class c1p.getClass();//将对象&#xff…

等差数列末项计算

等差数列末项计算 C语言代码C 代码Java代码Python代码 &#x1f490;The Begin&#x1f490;点点关注&#xff0c;收藏不迷路&#x1f490; 给出一个等差数列的前两项a1&#xff0c;a2&#xff0c;求第n项是多少。 输入 一行&#xff0c;包含三个整数a1&#xff0c;a2&#x…

PETRv2: A Unified Framework for 3D Perception from Multi-Camera Images

全文摘要 本文介绍了一种名为PETRv2的统一框架&#xff0c;用于从多视图图像中进行三维感知。该框架基于先前提出的PETR框架&#xff0c;并探索了时间建模的有效性&#xff0c;利用前一帧的时间信息来提高三维物体检测效果。作者在PETR的基础上扩展了三维位置嵌入&#xff08;…

【最新免费PPT制作并下载】Kimi PPT助手:智能化演示文稿生成,职场效率的革命性提升

最新免费PPT制作方法在这里&#xff01;下面我想向大家介绍一款能够极大提升我们工作效率的工具——Kimi PPT助手。 Kimi PPT助手&#xff1a;智能化演示文稿生成 Kimi PPT助手是由Moonshot AI推出的一款革命性产品&#xff0c;它通过人工智能技术&#xff0c;实现了PPT的一键…

蓝桥杯准备训练(lesson2 ,c++)

3.1 字符型 char //character的缩写在键盘上可以敲出各种字符&#xff0c;如&#xff1a; a &#xff0c; q &#xff0c; &#xff0c; # 等&#xff0c;这些符号都被称为字符&#xff0c;字符是⽤单引号括 起来的&#xff0c;如&#xff1a; ‘a’ &#xff0c; ‘b’ &…

C# 动态类型 Dynamic

文章目录 前言1. 什么是 Dynamic&#xff1f;2. 声明 Dynamic 变量3. Dynamic 的运行时类型检查4. 动态类型与反射的对比5. 使用 Dynamic 进行动态方法调用6. Dynamic 与 原生类型的兼容性7. 动态与 LINQ 的结合8. 结合 DLR 特性9. 动态类型的性能考虑10. 何时使用 Dynamic&…