C++ - set 和 map详解

目录

0. 引言

1. 关联式容器 

2. 键值对

 3. 树形结构

4. set

4.1 set 的定义

4.2 set 的构造 

4.3 set 的常用函数 

4.4 set 的特点 

5. multiset 

5.1 multiset 插入冗余数据

5.2 multiset - count 的使用 

6. map 

6.1 map 的定义 

6.2 map 的构造

6.3 map的常用函数 

6.4 map - operator[ ]

7. multimap 


0. 引言

在之前的博客中,我们已经介绍过 STL 中的部分容器,例如:vector,list,deque等,这些容器统称为序列式容器,因为其底层为线性序列的数据结构,里面存储的是元素本身。而本节我们介绍的 map 以及set 属于关联式容器,那什么是关联式容器?它与序列式容器有什么区别?

关联式容器也是用来存储数据的,与序列式容器不同的是,其里面存储的是结构的键值对,在数据检索时比序列式容器效率更高。

那么接下来我们一起逐步开始学习吧!

1. 关联式容器 

我们之前学习过的序列式容器底层为线性序列的数据结构,例如 list ,其节点是线性存储的,一个节点存储一个数据,但这些数据未必有序。而关联式容器则比较特殊,存储的是 <key, value>  的键值对, 这意味着可以按照键值大小 key 以某种规则放置在适当的位置,因此,关联式容器没有首位的概念,因此没有头插尾插等相关操作。

那什么是键值对呢?我们接着来看。

2. 键值对

键值对是用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代 表键值,value表示与key对应的信息。

比如:现在要建立一个英汉互译的字典,那该字典中必然 有英文单词与其对应的中文含义,而且,英文单词与其中文含义是一一对应的关系,即通过该应 该单词,在词典中就可以找到与其对应的中文含义。  

在标准库中,专门定义了键值对 pair :

//SGI 版 STL 中的实现
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) {}#ifdef __STL_MEMBER_TEMPLATEStemplate <class U1, class U2>pair(const pair<U1, U2>& p) : first(p.first), second(p.second) {}
#endif
};

其中, pair 中的 first 表示键值, second 表示实值,在给  关联式容器中插入数据时,就可以构建键值对 pair 对象。 

例如,下面构建了一个键值 keystring,实值 value int 的匿名键值对 pair 对象。 

pair<string, int>("hello", 123);

 此外,库中还设计了一个函数模板 make_pair, 可以根据传入的参数,去调用 pair 构建对象并返回。make_pair 实际会被编译器优化为 内联函数,因此不会造成过多消耗,可以放心使用

//make_pair 定义:
template <class T1,class T2>
pair<T1,T2> make_pair (T1 x, T2 y)
{return ( pair<T1,T2>(x,y) );
}
make_pair("hello", 123)

 3. 树形结构

在 C++ 中提供了四种树形结构的关联式容器。set / multiset / map / multimap。 

基于哈希表的,我们将在后续详细介绍。 

4. set

4.1 set 的定义

 set 实则是二叉搜索树中的 key 模型。key 模型主要的应用场景是判断在不在,例如:门禁系统,车库系统,检查文章中单词拼写是否正确等。

set 只包含实值 value,或者说, 实值就是键值,键值就是实值。

其参数的解释如下: 

template < class T,                        // 实值(键值)class Compare = less<T>,        // 存储依据,默认为升序class Alloc = allocator<T>      // 空间配置器> class set;

接着我们来看 set 的使用。 

4.2 set 的构造 

我们首先来看构造函数:

在这里我们创建时,需要指定实值的类型。 

#include<iostream>
#include<vector>
#include<set>
using namespace std;int main()
{vector<int> arr = {1,4,5,6,8,8,5,3,2,9};set<int> s1;//创建一个空的 setset<int> s2(arr.begin(), arr.end());//创建包含数据的 setcout << "s1: ";for (auto e : s1)cout << e << " ";cout << endl;cout << "s2: ";for (auto e : s2)cout << e << " ";cout << endl;return 0;
}

 

 与二叉搜索树一样, set 不支持数据冗余,冗余数据则无法插入,如果需要插入冗余数据,则需要使用 multiset,我们后续会讲到。

4.3 set 的常用函数 

功能作用
迭代器遍历容器
empty判断容器是否为空
size当前容器中的元素个数
max_set容器的最大容量
insert将元素插入到特定位置
erase删除指定元素
swap交换两个容器
clear清空容器中的所有元素
find查找实值是否存在并返回迭代器位置
count统计容器中指定键值的数量

下面我们实际演示相关函数的使用,代码如下: 

#include<iostream>
#include<vector>
#include<set>
using namespace std;int main()
{vector<int> arr = {1,4,5,6,8,3,2,9};set<int> s1(arr.begin(), arr.end());//创建包含数据的 set//迭代器遍历cout << "迭代器遍历结果: ";set<int>::iterator it = s1.begin();while (it != s1.end()){cout << *it << " ";++it;}cout << endl;cout << endl;cout << "empty(): " << s1.empty() << endl;cout << "size(): " << s1.size() << endl;cout << "max_size(): " << s1.max_size() << endl;//插入元素7cout << endl;cout << "insert(7): ";s1.insert(7);for (auto e : s1) cout << e << " ";cout << endl;//删除元素9cout << endl;cout << "erase(9): ";s1.erase(9);for (auto e : s1) cout << e << " ";cout << endl;//交换 查找 清理cout << endl;set<int> s2(arr.begin() + 3, arr.end());s1.swap(s2);cout << "s1: ";for (auto e : s1) cout << e << " ";cout << endl;cout << "s2: ";for (auto e : s2) cout << e << " ";cout << endl;cout << endl;cout << "s1.find(3): ";cout << (s1.find(3) != s1.end()) << endl;cout << endl;cout << "s2.clear(): " << endl;s2.clear();cout << "s1: ";for (auto e : s1) cout << e << " ";cout << endl;cout << "s2: ";for (auto e : s2) cout << e << " ";cout << endl;return 0;
}

结果如下: 

那么,count 在 set 中只能用作查找,因为 set 键值就是实值,因为其不允许冗余,因此只有一个键值。

#include <iostream>
#include <vector>
#include <set>
using namespace std;int main()
{vector<int> arr = { 1,4,5,6,8,3,2,9 };set<int> s1(arr.begin(), arr.end());for (int i = 0; i < 10; i++){if (s1.count(i))cout << i << " 在 set 中" << endl;elsecout << i << " 不在 set 中" << endl;}return 0;
}

我们也可以根据 set 的构造函数将其排为降序。 

#include <iostream>
#include <vector>
#include <set>
using namespace std;int main()
{vector<int> arr = { 1,4,5,6,8,3,2,9 };set<int, greater<int>> s1(arr.begin(), arr.end());for (auto e : s1)cout << e << " ";return 0;
}

最后需要注意的是,键值 key 不允许修改,因为如果改变了,会破坏二叉搜索树的原则,set 中的普通迭代器在本质上也是 const 迭代器。

4.4 set 的特点 

最后,我们总结一下 set 的特点: 

5. multiset 

multisetset 的唯一区别就在于 multiset 插入冗余数据时,不会失败。

由于 multiset 的操作与 set 基本相同,在这里我们主要演示区别。

5.1 multiset 插入冗余数据

先看代码,从中我们可以发现,multiset 允许数据冗余。

int main()
{vector<int> arr = { 1,3,3,4,5,6,7,5,6,7 };multiset<int> ms1(arr.begin(), arr.end());for (auto e : ms1)cout << e << " ";cout << endl;return 0;
}

此外,multiset 查找冗余的数据时,返回的是中序遍历中,第一次出现的元素 :

int main()
{vector<int> arr = { 1,3,3,4,5,6,7,5,6,7 };multiset<int> ms1(arr.begin(), arr.end());auto it = ms1.begin();while (it != ms1.end()){cout << *it << " | " << &*(it) << endl;++it;}cout << endl << endl;cout << "ms1.find(3): " << &*(ms1.find(3)) << endl;return 0;
}

5.2 multiset - count 的使用 

cout 在 multiset中。真正发挥了统计键值数的效果。 

int main()
{vector<int> arr = { 1,3,3,4,5,6,7,5,6,7 };multiset<int> ms1(arr.begin(), arr.end());for (int i = 0; i < 10; i++)cout << i << "在 multiset 中的数量: " << ms1.count(i) << endl;return 0;
}

6. map 

6.1 map 的定义 

map 是二叉搜索树改造后的 key / value 模型,是真正意义上的键值对,存在关于应用搜索的应用场景,例如:中英文互译字典,电话号码查询快速信息,电话号码+验证码等。

key / value 模型除了可以用来查找之外,还可以用来统计,其实就是哈希的思想,建立映射关系

模板中 key 就是键值对中的键值T 为键值对中的实值。 所以 map 也会使用到 pair 结构,其中first 表示键值second 表示实值。map 也存在迭代器,且为双向迭代器。

6.2 map 的构造

map 有以下构造方法: 

#include <iostream>
#include <vector>
#include <map>
using namespace std;int main()
{vector<pair<string, int>> arr = { make_pair("A", 65), make_pair("B", 66), make_pair("C", 67), make_pair("D", 68) };map<string, int> m1;//创建一个空的 mapmap<string, int> m2(arr.begin(), arr.end());//创建包含数据的 mapcout << "m1: " << endl;for (auto e : m1)cout << e.first << " | " << e.second << endl;cout << endl;cout << "m2: " << endl;for (auto e : m2)cout << e.first << " | " << e.second << endl;return 0;
}

这里在访问 map 中的键值和实值时,需要通过 pair 指定对象访问,例如这里的:e.first 。

6.3 map的常用函数 

功能作用
迭代器遍历容器
empty判断容器是否为空

size

当前容器中的元素数
max_size容器的最大容量
operator[ ]按照键值,访问实值,如果没有,则新插入
insert元素插入,根据特定条件插入至合适位置
erase删除指定元素
swap交换两个容器
clear清空容器中的所有元素
find查找实值是否存在并返回迭代器位置
count统计容器中指定键值的数量

这里函数的使用方法与 set 基本相同,但新增了一个 operator[ ]。 下面我们来看演示:

#include <iostream>
#include <vector>
#include <string>
#include <map>
using namespace std;int main()
{vector<pair<string, int>> arr{ make_pair("C", 67),make_pair("B", 66), make_pair("E",69), make_pair("A", 65), make_pair("D", 68), };map<string, int> m1(arr.begin(), arr.end());//迭代器遍历cout << "迭代器遍历结果: ";map<string, int>::iterator it = m1.begin();while (it != m1.end()){cout << "<" << it->first << ":" << it->second << "> ";++it;}cout << endl;//判空、求大小、解引用cout << endl;cout << "empty(): " << m1.empty() << endl;cout << "size(): " << m1.size() << endl;cout << "max_size(): " << m1.max_size() << endl;cout << "m1[""A""]: " << m1["A"] << endl;cout << "m1[""B""]: " << m1["B"] << endl;//插入元素cout  << endl;cout << "insert(""F"", 69): ";m1.insert(make_pair("F", 69));for (auto e : m1) cout << "<" << e.first << ":" << e.second << "> ";cout << endl;//删除元素cout << endl;cout << "erase(""C""): ";m1.erase("C");for (auto e : m1) cout << "<" << e.first << ":" << e.second << "> ";cout << endl;//交换、查找、清理cout << endl;map<string, int> m2(arr.begin() + 1, arr.end()-1);m1.swap(m2);cout << "m1.swap(m2)" << endl;cout << "m1: ";for (auto e : m1) cout << "<" << e.first << ":" << e.second << "> ";cout << endl;cout << "m2: ";for (auto e : m2) cout << "<" << e.first << ":" << e.second << "> ";cout << endl;cout << endl;cout << "m1.find(""B""): ";cout << (m1.find("B") != m1.end()) << endl;cout << endl;cout << "m2.clear()" << endl;m2.clear();cout << "m1: ";for (auto e : m1) cout << "<" << e.first << ":" << e.second << "> ";cout << endl;cout << "m2: " << endl;for (auto e : m2) cout << "<" << e.first << ":" << e.second << "> ";cout << endl;return 0;
}

 map 依然不允许数据冗余,如果需要插入重复的数据,则需要使用 multimap

map 插入操作返回值是一个 pair ,因为既要表示是否成功,也要返回插入成功的迭代器 

map 中 find 和 count 跟 set 一样。 可以用来判断元素是否存在,find 返回迭代器,count 返回则是键值数。此外,map可以根据普通迭代器修改实值。

6.4 map - operator[ ]

operator[ ]返回的是当前键值对应的实值,如果当前键值不存在,则会插入新的键值对。

int main()
{vector<string> word = { "西瓜", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉", "梨" };map<string, int> table;for (auto& e : word)table[e]++;for (auto e : table)cout << "<" << e.first << ":" << e.second << ">" << endl;return 0;
}

operator[ ] 兼顾了这几种功能:插入、修改、插入+修改、查找。

6.5 map的特点

7. multimap 

multimap 允许键值出现冗余。 

由于 multimap 允许出现多个重复的键值,因此无法使用 operator[ ],因为 operator[ ] 无法确认调用者的意图 -> 不知道要返回哪个键 对应的实值。

除此此外 multimap  与 map 没有区别。

最后: 

想说明的是:multiset / multimap 用的较少,重点掌握 set / map 即可。


 

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

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

相关文章

Python基于Django的微博热搜、微博舆论可视化系统

博主介绍&#xff1a;✌IT徐师兄、7年大厂程序员经历。全网粉丝15W、csdn博客专家、掘金/华为云//InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#x1f3…

银河麒麟高级服务器操作系统adb读写缓慢问题分析

1.问题环境 处理器&#xff1a; HUAWEI Kunpeng 920 5251K 内存&#xff1a; 512 GiB 整机类型/架构&#xff1a; TaiShan 200K (Model 2280K) BIOS版本&#xff1a; Byosoft Corp. 1.81.K 内核版本 4.19.90-23.15.v2101.ky10.aarch64 第三方应用 数据库 2.问题…

数据可视化高级技术Echarts(桑基图入门)

目录 一、什么是桑基图 二、基本特征 三、设计注意事项 四、使用Echarts进行初级绘制 1.首先不能忘记五个基本步骤 2.绘制的时需要将图像类型series.type设定为sankey类型。 一、什么是桑基图 桑基图&#xff08;Sankey diagram&#xff09;&#xff0c;即桑基能量分流图&…

【高录用-快速见刊】2024年数字化经济与金融创新国际学术会议(ICDEFI 2024)

会议简介 2024年数字经济与金融创新国际学术会议即将召开。此次会议旨在汇集全球数字经济与金融创新领域的专家学者&#xff0c;共同探讨数字经济的发展趋势以及金融创新的路径。与会者将分享前沿研究成果&#xff0c;讨论数字技术在金融领域的应用与创新&#xff0c;并推动数…

主线程捕获子线程异常

正常情况下使用多线程出现异常时&#xff0c;都是按照单个任务去处理异常&#xff0c;在线程间不需要通信的情况下&#xff0c;任务之间互不影响&#xff0c;主线程也不必知道子线程是否发成异常。 那么只需要处理子线程异常即可 Task.Run(() > {try{throw new Exception(&…

OpenHarmony轻量系统开发【2】源码下载和开发环境

2.1源码下载 关于源码下载的&#xff0c;读者可以直接查看官网&#xff1a; https://gitee.com/openharmony/docs/tree/master/zh-cn/release-notes 本文这里做下总结&#xff1a; &#xff08;1&#xff09;注册码云gitee账号。 &#xff08;2&#xff09;注册码云SSH公钥…

112 arcpy 发布 mxd地图文件 到 arcgis服务器 为 地图服务

前言 此文档主要是记录一下 最近的一次机遇 arcpy 来发布 地图文件到 arcgis服务器 上面 arcpy 主要是来自于 ArcGIS_Desktop_105_154030.zip 安装之后会在 python 的安装目录 安装另外的一份带 arcgis 的 python 环境, 然后 本文相关类库 也是基于 这个 arcpy 的 python 环境…

jenkins(docker)安装及应用

jenkins Jenkins是一个开源的、提供友好操作界面的持续集成(CI)工具&#xff0c;起源于Hudson&#xff08;Hudson是商用的&#xff09;&#xff0c;主要用于持续、自动的构建/测试软件项目、监控外部任务的运行&#xff08;这个比较抽象&#xff0c;暂且写上&#xff0c;不做解…

【算法刷题 | 回溯思想 04】4.15(分割回文串)

文章目录 7.分割回文串7.1题目7.2解法&#xff1a;回溯7.2.1回溯思路&#xff08;1&#xff09;函数返回值以及参数&#xff08;2&#xff09;终止条件&#xff08;3&#xff09;遍历过程 7.2.2代码 7.分割回文串 7.1题目 给你一个字符串 s&#xff0c;请你将 s 分割成一些子…

【蓝桥·算法双周赛 第 9 场 小白入门赛】盖印章【算法赛】题解(数学+解方程)

思路 考虑到题目中的规则&#xff0c;每个印章图案的边必须和网格图边重合&#xff0c;网格图上的每一个格子最多只能被一个印章图案覆盖&#xff0c;印章的图案在网格图上必须是完整的。这意味着每个印章图案都会覆盖一个独立的、完整的区域&#xff0c;且这些区域不会相互重…

OpenHarmony实战开发-页面深色模式适配。

介绍 本示例介绍在开发应用以适应深色模式时&#xff0c;对于深色和浅色模式的适配方案&#xff0c;采取了多种策略如下&#xff1a; 1. 固定属性适配&#xff1a;对于部分组件的颜色属性&#xff0c;如背景色或字体颜色&#xff0c;若保持不变&#xff0c;可直接设定固定色值…

Linux网络基础(一)

网络发展 对于我们国家来讲&#xff0c;网络的发展&#xff0c;不仅仅是互联网公司在发展&#xff0c;提供重要推动力的还有三大运商 随之而来的是新设备的诞生。比如集线器&#xff0c;网线&#xff0c;光纤&#xff0c;调制解调器&#xff0c;路由器&#xff0c;防火墙&am…

Nginx转发请求错误

说明&#xff1a;记录一次使用Nginx转发请求的错误&#xff1b; 场景 公司内部有两台服务器都跑了后端项目&#xff0c;在使用Nginx做请求分发时&#xff0c;我发现其中有台服务器一直没有处理请求&#xff08;没打印相关的日志信息&#xff09;&#xff0c;于是我修改了下Ng…

NVIDIA全系列GPU技术路线演进分析

NVIDIA GPU 架构梳理 近期深入研究并行计算&#xff0c;需探究底层硬件精髓。高性能计算界&#xff0c;英伟达显卡稳居霸主地位。本文旨在梳理NVIDIA GPU架构之演进历程&#xff0c;助您洞悉其技术脉络&#xff0c;把握未来计算趋势。 目录&#xff1a; NVIDIA GPU架构历经数次…

代码随想录Day41:动态规划Part3

Leetcode 343. 整数拆分 讲解前&#xff1a; 毫无头绪 讲解后&#xff1a; 这道题的动态思路一开始很不容易想出来&#xff0c;虽然dp数组的定义如果知道是动态规划的话估摸着可以想出来那就是很straight forward dp定义&#xff1a;一维数组dp[i], i 代表整数的值&#xf…

WEB前端-笔记

目录 一、字体 二、背景图片 三、显示方式 四、类型转换 五、相对定位 六、绝对定位 七、固定定位 八、Index 九、粘性定位 十、内边距 十一、外边距 十二、边框 十三、盒子尺寸计算问题 十四、清楚默认样式 十五、内容溢出 十六、外边距的尺寸与坍塌 十七、行…

构建高效可靠的Zabbix监控系统

前言 监控系统对于确保系统稳定性、性能优化以及故障排除至关重要。zabbix 作为一款功能强大且灵活的监控解决方案&#xff0c;可以通过一个友好的界面进行浏览整个网站所有服务器状态、在 web 前端方便查看数据以及可以回溯事故时的问题和告警。 目录 一、zabbix 监控介绍 …

electron打包编译国产统信uos系统 arm架构 x86架构 linux mac等环境

electron v21版本以上统信UOS会提示gbm_bo_map错误&#xff0c;可使用v8~v21版本的electron 打包linux包需要再linux系统下运行编译&#xff0c;arch可以指定架构 如果要在统信uos上运行&#xff0c;需要打包成deb格式&#xff0c;在target中修改成deb 或者用第三方软件把app…

建立时间/保持时间为负是什么情况

目录 建立时间为负保持时间为负参考 在说明建立时间和保持时间为何为负的情况下&#xff0c;首先可以看看建立时间Tsu和保持时间Th的由来&#xff0c;可参考如下两篇文章&#xff1a; 建立时间和保持时间理解_为什么要满足建立时间和保持时间-CSDN博客 ic基础|时序篇&#xff…

基于Springboot的旅游管理系统

基于SpringbootVue的旅游管理系统的设计与实现 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringbootMybatis工具&#xff1a;IDEA、Maven、Navicat 系统展示 用户登录 首页展示 旅游方案展示 旅游资讯 后台管理员登录 后台管理页面首页 用户管理 …