C++从入门到起飞之——(multi)set与(multi)map的的使用 全方位剖析!

🌈个人主页:秋风起,再归来~
🔥系列专栏:C++从入门到起飞          
🔖克心守己,律己则安

目录

1. 序列式容器和关联式容器

2. set系列的使⽤

2.1 set和multiset参考⽂档

2.2 set类的介绍

2.3 set的构造和迭代器

 2.4 set的增删查

2.5 multiset和set的差异 

3、map系列的使用

3.1 map和multimap参考⽂档

3.2 map类的介绍

3.3 pair类型介绍

3.4 map的增删查

3.5 map的数据修改

3.6 multimap和map的差异

4、完结散花


1. 序列式容器和关联式容器

前⾯我们已经接触过STL中的部分容器如:string、vector、list、deque、array、forward_list等,这 些容器统称为序列式容器,因为逻辑结构为线性序列的数据结构,两个位置存储的值之间⼀般没有紧 密的关联关系,⽐如交换⼀下,他依旧是序列式容器。顺序容器中的元素是按他们在容器中的存储位 置来顺序保存和访问的。

关联式容器也是⽤来存储数据的,与序列式容器不同的是,关联式容器逻辑结构通常是⾮线性结构, 两个位置有紧密的关联关系,交换⼀下,他的存储结构就被破坏了。顺序容器中的元素是按关键字来 保存和访问的。关联式容器有map/set系列和unordered_map/unordered_set系列。

本文讲解的map和set底层是红⿊树,红⿊树是⼀颗平衡⼆叉搜索树。set是key搜索场景的结构, map是key/value搜索场景的结构。

2. set系列的使⽤

2.1 set和multiset参考⽂档

https://legacy.cplusplus.com/

2.2 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;

• set的声明如上,T就是set底层关键字的类型

• set默认要求T⽀持⼩于⽐较,如果不⽀持或者想按⾃⼰的需求⾛可以⾃⾏实现仿函数传给第⼆个模 版参数

• set底层存储数据的内存是从空间配置器申请的,如果需要可以⾃⼰实现内存池,传给第三个参 数。

• ⼀般情况下,我们都不需要传后两个模版参数。

• set底层是⽤红⿊树实现,增删查效率是O(logN),迭代器遍历是⾛的搜索树的中序,所以是有序 的。 

• 前⾯部分我们已经学习了vector/list等容器的使⽤,STL容器接⼝设计,⾼度相似,所以这⾥我们 就不再⼀个接⼝⼀个接⼝的介绍,⽽是直接带着⼤家看⽂档,挑⽐较重要的接⼝进⾏介绍。

2.3 set的构造和迭代器

set的构造我们关注以下⼏个接⼝即可。

set的⽀持正向和反向迭代遍历,遍历默认按升序顺序,因为底层是⼆叉搜索树,迭代器遍历⾛的中 序;⽀持迭代器就意味着⽀持范围for,set的iterator和const_iterator都不⽀持迭代器修改数据,修改 关键字数据,破坏了底层搜索树的结构。

(1)无参数默认构造

explicit set (const key_compare& comp = key_compare(),const allocator_type& alloc = allocator_type());explicit set (const allocator_type& alloc);

上面的构造函数被声明为explicit,这可阻止它们被用来执行隐式类型转化,但它们仍可被用来执行显示类型转换。 

被声明为explicit的构造函数通常比其兄弟non-explicit更受欢迎,因为它们禁止编译器执行非预期(往往也不被期望)的类型转换。除非我有一个好理由允许构造函数被用于隐式类型转换,否则我会把它声明为explicit。

(2) 迭代器区间构造

set (InputIterator first, InputIterator last,const key_compare& comp = key_compare(),const allocator_type& = allocator_type());

(3) 拷⻉构造

void insert (initializer_list il);
set (const set& x);
set (const set& x, const allocator_type& alloc);

 (4) 列表构造

set (initializer_list<value_type> il,const key_compare& comp = key_compare(),const allocator_type& alloc = allocator_type());

 2.4 set的增删查

set的增删查关注以下⼏个接⼝即可:

(1)单个数据插⼊,如果已经存在则插⼊失败

pair<iterator,bool> insert (const value_type& val);

pair其实是std库里面的一个类模版

 

set<int> s1;
auto it = s1.insert(1);
cout << *it.first << endl;
cout << it.second<< endl;
cout <<typeid(it).name()<< endl;

下面我们还会再提到pair类型 

(2)列表插⼊,已经在容器中存在的值不会插⼊

void insert (initializer_list<value_type> il);

(3)迭代器区间插⼊,已经在容器中存在的值不会插⼊

template <class InputIterator>void insert (InputIterator first, InputIterator last);

(4)查找val,返回val所在的迭代器,没有找到返回end()

iterator find (const value_type& val);

(5)查找val,返回Val的个数

size_type count (const value_type& val) const;

(6)删除⼀个迭代器位置的值

iterator erase (const_iterator position);

(7)删除val,val不存在返回0,存在返回1

size_type erase (const value_type& val);

(8)删除⼀段迭代器区间的值

iterator erase (const_iterator first, const_iterator last);

(9)返回⼤于等val位置的迭代器

iterator lower_bound (const value_type& val) const;

(10)返回⼤于val位置的迭代器

iterator upper_bound (const value_type& val) const;

2.5 multiset和set的差异 

multiset和set的使⽤基本完全类似,主要区别点在于multiset⽀持值冗余,那么insert/find/count/erase都围绕着⽀持值冗余有所差异,具体参看下⾯的样例代码理解。

// 相⽐set不同的是,multiset是排序,但是不去重 
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;// 相⽐set不同的是,x可能会存在多个,find查找中序的第⼀个 
int x;
cin >> x;
auto pos = s.find(x);
while (pos != s.end() && *pos == x)
{cout << *pos << " ";++pos;
}
cout << endl;
// 相⽐set不同的是,count会返回x的实际个数 
cout << s.count(x) << endl;
// 相⽐set不同的是,erase给值时会删除所有的x 
s.erase(x);
for (auto e : s)
{cout << e << " ";
}
cout << endl;

3、map系列的使用

3.1 map和multimap参考⽂档

https://legacy.cplusplus.com/reference/map/map/?kw=map

3.2 map类的介绍

map的声明如下,Key就是map底层关键字的类型,T是map底层value的类型,set默认要求Key⽀持⼩于⽐较,如果不⽀持或者需要的话可以⾃⾏实现仿函数传给第⼆个模版参数,map底层存储数据的 内存是从空间配置器申请的。⼀般情况下,我们都不需要传后两个模版参数。map底层是⽤红⿊树实 现,增删查改效率是O(logN) ,迭代器遍历是⾛的中序,所以是按key有序顺序遍历的。

template < class Key, // map::key_typeclass T, // map::mapped_typeclass Compare = less<Key>, // map::key_compareclass Alloc = allocator<pair<const Key,T> > // 
map::allocator_type> class map;

3.3 pair类型介绍

map底层的红⿊树节点中的数据,使⽤pair存储键值对数据。 

typedef pair<const Key, T> value_type;
template <class T1, class T2>
struct pair 
{typedef T1 first_type;typedef T2 second_type;T1 first;T2 second;pair(): first(T1()), second(T2()){}pair(const T1& a, const T2& b): first(a), second(b){}template<class U, class V> pair (const pair<U,V>& pr): first(pr.first), second(pr.second){}
};

3.4 map的增删查

map的增删查关注以下⼏个接⼝即可:

 map增接⼝,插⼊的pair键值对数据,跟set所有不同,但是查和删的接⼝只⽤关键字key跟set是完全类似的,不过find返回iterator,不仅仅可以确认key在不在,还找到key映射的value,同时通过迭代 还可以修改value

Member types
key_type -> The first template parameter (Key)
mapped_type -> The second template parameter (T)
value_type -> pair<const key_type,mapped_type>// 单个数据插⼊,如果已经key存在则插⼊失败,key存在相等value不相等也会插⼊失败 
pair<iterator,bool> insert (const value_type& val);// 列表插⼊,已经在容器中存在的值不会插⼊ 
void insert (initializer_list<value_type> il);// 迭代器区间插⼊,已经在容器中存在的值不会插⼊ 
template <class InputIterator>
void insert (InputIterator first, InputIterator last);// 查找k,返回k所在的迭代器,没有找到返回end() 
iterator find (const key_type& k);// 查找k,返回k的个数 
size_type count (const key_type& k) const;// 删除⼀个迭代器位置的值 
iterator erase (const_iterator position);// 删除k,k存在返回0,存在返回1 
size_type erase (const key_type& k);// 删除⼀段迭代器区间的值 
iterator erase (const_iterator first, const_iterator last);// 返回⼤于等k位置的迭代器 
iterator lower_bound (const key_type& k);// 返回⼤于k位置的迭代器 
const_iterator lower_bound (const key_type& k) const;

我们要注意到map是要插入一个pair类型的,我们可以用匿名对象的方式插入:

map<string , string> m;
m.insert(pair<string, string>("left", "左边"));

 不过这种方法太麻烦了,所以我们通常用make_pair的方式:

maek_pair是定义在std上的一个内联函数模版:

template <class T1,class T2>
inline pair<T1,T2> make_pair (T1 x, T2 y)
{return ( pair<T1,T2>(x,y) );
}
map<string, string> m;
m.insert(make_pair("left", "左边"));

3.5 map的数据修改

前⾯我提到map⽀持修改mapped_type数据,不⽀持修改key数据,修改关键字数据,破坏了底层搜 索树的结构。

map第⼀个⽀持修改的⽅式时通过迭代器,迭代器遍历时或者find返回key所在的iterator修改,map 还有⼀个⾮常重要的修改接operator[],但是operator[]不仅仅⽀持修改,还⽀持插⼊数据和查找数据,所以他是⼀个多功能复合接

需要注意从内部实现⻆度,map这⾥把我们传统说的value值,给的是T类型,typedef为 mapped_type。⽽value_type是红⿊树结点中存储的pair键值对值。⽇常使⽤我们还是习惯将这⾥的 T映射值叫做value。

Member types
key_type -> The first template parameter (Key)
mapped_type -> The second template parameter (T)
value_type -> pair<const key_type,mapped_type>// 查找k,返回k所在的迭代器,没有找到返回end(),如果找到了通过iterator可以修改key对应的
mapped_type值 
iterator find (const key_type& k);// ⽂档中对insert返回值的说明 
// The single element versions (1) return a pair, with its member pair::first 
set to an iterator pointing to either the newly inserted element or to the 
element with an equivalent key in the map. The pair::second element in the pairis set to true if a new element was inserted or false if an equivalent key 
already existed.// insert插⼊⼀个pair<key, T>对象 // 1、如果key已经在map中,插⼊失败,则返回⼀个pair<iterator,bool>对象,返回pair对象
first是key所在结点的迭代器,second是false // 2、如果key不在在map中,插⼊成功,则返回⼀个pair<iterator,bool>对象,返回pair对象
first是新插⼊key所在结点的迭代器,second是true // 也就是说⽆论插⼊成功还是失败,返回pair<iterator,bool>对象的first都会指向key所在的迭
代器 // 那么也就意味着insert插⼊失败时充当了查找的功能,正是因为这⼀点,insert可以⽤来实现
operator[]// 需要注意的是这⾥有两个pair,不要混淆了,⼀个是map底层红⿊树节点中存的pair<key, T>,另
⼀个是insert返回值pair<iterator,bool>
pair<iterator,bool> insert (const value_type& val);
mapped_type& operator[] (const key_type& k);// operator的内部实现 
mapped_type& operator[] (const key_type& k)
{// 1、如果k不在map中,insert会插⼊k和mapped_type默认值,同时[]返回结点中存储
mapped_type值的引⽤,那么我们可以通过引⽤修改返映射值。所以[]具备了插⼊+修改功能 // 2、如果k在map中,insert会插⼊失败,但是insert返回pair对象的first是指向key结点的
迭代器,返回值同时[]返回结点中存储mapped_type值的引⽤,所以[]具备了查找+修改的功能 pair<iterator, bool> ret = insert({ k, mapped_type() });iterator it = ret.first;return it->second;
}

3.6 multimap和map的差异

multimap和map的使⽤基本完全类似,主要区别点在于multimap⽀持关键值key冗余,那么 insert/find/count/erase都围绕着⽀持关键值key冗余有所差异,这⾥跟set和multiset完全⼀样,⽐如 find时,有多个key,返回中序第⼀个。其次就是multimap不⽀持[],因为⽀持key冗余,[]就只能⽀持插⼊了,不能⽀持修改。 

4、完结散花

好了,这期的分享到这里就结束了~

如果这篇博客对你有帮助的话,可以用你们的小手指点一个免费的赞并收藏起来哟~

如果期待博主下期内容的话,可以点点关注,避免找不到我了呢~

我们下期不见不散~~

​​​​

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

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

相关文章

打印自然常数E

自然常数E 自然常数&#xff0c;符号e&#xff0c;为数学中一个常数&#xff0c;是一个无限不循环小数&#xff0c;且为超越数&#xff0c;其值约为2.718281828459045。它是自然对数函数的底数。 我们打印表达式(11/x)的x次方的值以及获取第一次大于2.718的正整数 新建C#控制…

Linux系统:Ubuntu上安装Chrome浏览器

Ubuntu系统版本&#xff1a;23.04 在Ubuntu系统上安装Google Chrome浏览器&#xff0c;可以通过以下步骤进行&#xff1a; 终端输入以下命令&#xff0c;先更新软件源&#xff1a; sudo apt update 或 sudo apt upgrade终端输入以下命令&#xff0c;下载最新的Google Chrome .…

HarmonyNext保存Base64文件到Download下

本文介绍如何保存Base64的文件到Download下 参考文档地址&#xff1a; 保存用户文件-Harmony Next 用到的是DOWNLOAD模式保存文件 用户在使用save接口时&#xff0c;可以将pickerMode配置为DOWNLOAD模式&#xff0c;该模式下会拉起授权接口&#xff0c;用户确认后会在公共路径…

net core 微信公众号发送模板消息完整实现

第一完整看一下微信官方的文档 链接&#xff1a;开发前必读 / 首页 (qq.com) 想要发送模板消息分为一下几步 第一步&#xff1a;想要发消息需要有这几个参数&#xff0c; openid&#xff0c;这是给谁发消息 access_token&#xff0c;调用接口必要的 appid、secret 这两个是生…

如何替换OCP节点(二):使用 antman脚本 | OceanBase应用实践

前言&#xff1a; OceanBase Cloud Platform&#xff08;简称OCP&#xff09;&#xff0c;是 OceanBase数据库的专属企业级数据库管理平台。 在实际生产环境中&#xff0c;OCP的安装通常是第一步&#xff0c;先搭建OCP平台&#xff0c;进而依赖OCP来创建、管理和监控我们的生…

开放式蓝牙耳机哪个品牌好用?开放式耳机排行榜测评!

开放式耳机&#xff0c;因其特殊的不入耳佩戴模式&#xff0c;让使用者在享受音乐或者进行通话的过程中&#xff0c;依然可以对外界声音保持敏感。在户外运动场景下&#xff0c;这种特性优势尽显&#xff0c;既保证了耳机佩戴的稳定和舒适&#xff0c;又提高了运动的安全性。为…

如何做好项目管理,实现高效协作?

项目管理难&#xff0c;难于上青天&#xff01; 小卫&#xff0c;某服装生产企业项目经理&#xff0c;说到项目难管理&#xff0c;他有话要说&#xff1a; 做项目&#xff0c;看似简单&#xff0c;不同部门各司其职&#xff0c;但也正因为涉及跨部门协作&#xff0c;管理难度也…

Qt:图片文字转base64程序

目录 一.Base64 1.编码原理 2.应用场景 3.优点 4.限制 5.变种 二.文字与Base64互转 1.ui设计 2.文字转Base64 3.Base64转文字 三.图片与Base64互转 1.ui设计 2.选择图片与图片路径 3.图片转Base64 4.Base64转图片 四.清空设置 五.效果 六.代码 base64conver…

基于SpringBoot+Vue+uniapp的时间管理小程序的详细设计和实现(源码+lw+部署文档+讲解等)

详细视频演示 请联系我获取更详细的演示视频 项目运行截图 技术框架 后端采用SpringBoot框架 Spring Boot 是一个用于快速开发基于 Spring 框架的应用程序的开源框架。它采用约定大于配置的理念&#xff0c;提供了一套默认的配置&#xff0c;让开发者可以更专注于业务逻辑而不…

大厂面试一上来就手撕 Transformer,心凉半截

在这两年&#xff0c;尤其是大模型问世之后&#xff0c;有关 Transformer 的面试题不仅数量众多&#xff0c;而且颇具新意。 今日&#xff0c;我将分享 18 道 Transformer 高频面试题&#xff08;如需获取更多专业面试题&#xff0c;扫描文末二维码即可&#xff09;&#xff0…

关于为什么蒸馏后的小模型和一开始的小模型的区别是什么?

起初&#xff0c;我想写这个博客是因为无意间看到了一个采访&#xff0c;无意间浏览才发现这段说的给我一种恍然大悟的感觉。 主持人提问&#xff1a;训练一个大模型&#xff0c;然后再将其压缩蒸馏成一个小模型&#xff0c;那和直接训练一个小模型&#xff0c; 这两者的区别是…

再Android10上实现检测AHD摄像头是否接入

项目有个需要&#xff0c;需要知道tp9951是否接入AHD摄像头 1&#xff0c;驱动层可以通过读取寄存器的值来检测是否接入AHD摄像头 tp9951_write_reg(0x40, 0x00); //select decoder page tp9951_write_reg(0x41, ch); val tp9951_read_reg(TP_INPUT_STATUS_REG);…

【含文档】基于Springboot+Vue的仓库管理系统设计与实现(含源码+数据库+lw)

1.开发环境 开发系统:Windows10/11 架构模式:MVC/前后端分离 JDK版本: Java JDK1.8 开发工具:IDEA 数据库版本: mysql5.7或8.0 数据库可视化工具: navicat 服务器: SpringBoot自带 apache tomcat 主要技术: Java,Springboot,mybatis,mysql,vue 2.视频演示地址 3.功能 系统定…

ZBrush和3D-Coat各自的优缺点是什么?

zbrush支持的模型面数高英文界面&#xff0c;3d coat支持的模型面数比zbrsh低有中文界 ZBrush优缺点 1、ZBrush优点&#xff1a; zbrush是高精度建模poser制作的首选。可搭配雕刻版使用&#xff0c;主要为烘焙高细节的铁图建模。因为是高精度模型&#xff0c;不适用于动画和游…

MySQL【知识改变命运】08

数据库约束 1&#xff1a;约束的几个类型2&#xff1a;NOT NULL非空约束3&#xff1a;UNIQUE 唯⼀约束4&#xff1a;PRIMARY KEY 主键约束4.1:回顾 5&#xff1a;FOREIGN KEY 外键约束5.1&#xff1a;创建班级表(主表)&#xff0c;并初始化数据5.2&#xff1a;重构学⽣表(从表)…

移动云智算平台,斩获两大国际知名设计奖项

近日&#xff0c;移动云一站式智算平台从全球激烈的竞争中脱颖而出&#xff0c;斩获2024年缪斯设计奖与法国设计奖两项国际知名设计大奖。这两大奖项在全球设计与创新领域颇具影响力&#xff0c;致力于表彰来自全球的优秀和原创设计作品。此次获奖&#xff0c;体现了移动云出色…

5. Node.js Http模块

2.4 Http模块 2.4.1创建Http服务端 //1.导入http模块 let http=require(http)//2.创建服务对象 let server=http.createServer((request,response)=>{console.log(request.method) //获取请求方式console.log(request.url) //获取请求url(路径和参数部份…

JavaWeb Servlet--09深入:注册系统04--修改页面

修改页面 分析&#xff1a;点击修改超链接&#xff0c;就跳转到一个修改界面&#xff0c;要显示原本的数据&#xff0c;且密码显示出来&#xff0c;在该页面将对用户的数据的进行修改&#xff0c;最后提交。 开始业务&#xff1a; 1.在web下创建一个修改界面update.jsp 写法…

蓝牙资讯|苹果AirPods Pro 2耳机推送开发者Beta固件

科技媒体 MacRumors 报道&#xff0c;苹果公司邀请开发者&#xff0c;针对 Lightning 和 USB-C 接口的 AirPods Pro 2 耳机&#xff0c;推送了新的 7B5013d 固件版本&#xff0c;较之前的 7B5013c 有所提升。 苹果未来会向所有 AirPods Pro 2 用户推送本次固件更新&#xff0…

基于OpenFOAM和Python的流场动态模态分解:从数据提取到POD-DMD分析

本文探讨了Python脚本与动态模态分解(DMD)的结合应用。我们将利用Python对从OpenFOAM模拟中提取的二维切片数据进行DMD计算。这种方法能够有效地提取隐藏的流动模式,深化对流体动力学现象的理解。 使用开源CFD软件OpenFOAM,有两种方法可以对CFD数据进行DMD计算。第一种方法是直…