【C++】map和set的介绍和使用

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

序列式容器
底层为线性序列的数据结构, 里面存储的是元素本身 。如vector/list/string/deque/forward_list。
关联式容器
也是用来存储数据的,于序列式容器不同的是, 里面存储的是<key,value>结构的键值对 ,在数据检索时比序列式容器效率高。如map/set/unordered_map/unordered_set。

2.树形结构的关联式容器

根据应用场景的不同,STL总共实现了两种不同结构的管理式容器 树型结构 哈希结构 。树型结构的关联式容器主要有四种:map、set、multimap、multiset。这四种容器的共同点是: 使用平衡搜索树(即红黑树)作为其底层结果,容器中的元素是一个有序的序列。 下面一依次介绍每一个容器。

2.set

set文档介绍

set底层是 二叉搜索树的K模型, map底层是 二叉搜索树的KV模型,关于二叉搜索树可以查看上篇 【二叉树进阶】二叉搜索树-CSDN博客

T: set 中存放元素的类型,实际在底层存储 <value, value> 的键值对。
Compare set 中元素默认按照小于来比较
Alloc set 中元素空间的管理方式,使用 STL 提供的空间配置器管理

简单使用:

void test_set1()
{set<int> s;s.insert(1);s.insert(3);s.insert(4);s.insert(4);s.insert(5);//迭代器遍历set<int>::iterator it = s.begin();while (it != s.end()){cout << *it << " ";++it;}cout << endl;//范围for遍历for (auto& e : s){cout << e << " ";}cout << endl;//拷贝构造set<int> copy(s);for (auto& e : copy){cout << e << " ";}
}

可以看到,set具有排序+去重的效果,set底层就是搜索树,中序是有序的。

删除操作:

//1、使用迭代器方式删除
set<int>::iterator pos = s.find(40);
if (pos != s.end())
{s.erase(pos);
}
//2、直接删除
s.erase(40);
//3、使用算法里的find删除
set<int>::iterator pos = find(s.begin(),s.end(),40);

说明:

使用迭代器方式删除,迭代器的位置必须是有效位置,否则,如果不检查,就会报错。

直接删除,值存在就删除,不存在就不删。

使用set自己的find和算法里的find有什么区别?

效率的问题。算法里的find效率是O(N),set底层是搜索树,find效率为O(log_2 N)。

算法里的find是用函数模板实现的,目的是让任何类型的迭代器都可以使用。

2.1set特点

1、set是K模型,主要用于查找“在不在”,特点就是快,因为底层是搜索树。

例如:

假设要把中国所有人的身份信息放进set中,查找一个人,只需要查找31次就可以了。

2^10*2^10*2^10~=2^30==十亿。

2、增、删、查时间复杂度为O(log_2 N)。

3、修改:不允许修改,因为修改就会破坏搜索树的结构。

2.2set应用举例

set为K模型,主要应用查找“在不在”的问题,这里举一个“简易字典”的例子。

在项目工程中,一般要求不展开std。(为了演示,此示例下为也没有展开std)

简易字典:

void test_map2()
{std::map<std::string, std::string> dict;dict.insert(pair<std::string, std::string>("left", "左边"));dict.insert(pair<std::string, std::string>("sort", "排序"));dict.insert(pair<std::string, std::string>("string", "排序"));std::map < std::string, std::string>::iterator it = dict.begin();while (it != dict.end()){cout << it->first << ":" << it->second << endl;++it;}cout << endl;
}

按key的ASCII从小到大排序(走的是二叉树的中序为有序)。

3.map

map文档介绍

key: 键值对中 key 的类型。
T : 键值对中 value 的类型。
Compare: 比较器的类型, map 中的元素是按照 key 来比较的,缺省情况下按照小于来比较,一般情况下( 内置类型元素 ) 该参数不需要传递,如果无法比较时 ( 自定义类型 ) ,需要用户自己显式传递比较规则( 一般情况下按照函数指针或者仿函数来传递 )。
Alloc :通过空间配置器来申请底层空间,不需要用户传递,除非用户不想使用标准库提供的
空间配置器。

简单使用:

这样插入会报错。来看一下insert的说明。

 这里insert的参数是value_type& val,value_type就是pair。

再继续探索insert之前,就引入了键值对这一概念。

3.1键值对pair

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

SGI-STL中关于键值对的定义:

为什么map的insert参数要传pair呢?与它的遍历有关。

	map<int, int> m;m.insert(pair<int,int>(1, 1));m.insert(pair<int,int>(2, 2));m.insert(pair<int,int>(3, 3));m.insert(pair<int,int>(4, 4));//遍历map<int, int>::iterator it = m.begin();while (it != m.end()){cout << *it << " ";++it;}cout << endl;

这种方式会报错,map中存有两个值,*it会返回两个值,C++是不允许一次返回两个值的,要返回两个值,把它们构成一个结构返回可以。

pair就相当于一个结构。*it返回的是一个pair,是一个key,value的结构体。

上述会报错的原因也就是*it是一个pair,自定义类型没有重载,不支持输出。

//正确遍历方式
while (it != m.end())
{cout << (*it).first << ":" << (*it).second << endl;//cout << it->first << ":" << it->second << endl;++it;
}

说明:

operator* 返回值是节点中值的引用,

operator-> 返回值是节点中值的指针,也就是pair<k,v>指针,这里"it->first"本应该是"it->->first",编译器为了可读性省略了一个"->"。

insert参数也可以用make_pair。

3.2make_pair

void test_map1()
{map<int, int> m;pair<int, int> a(0,0);m.insert(a);m.insert(pair<int,int>(1, 1));m.insert(pair<int,int>(2, 2));m.insert(pair<int,int>(3, 3));//pair构造函数,构造一个匿名对象m.insert(make_pair(5, 5));//函数模板构造一个pair对象//遍历map<int, int>::iterator it = m.begin();while (it != m.end()){cout << (*it).first << ":" << (*it).second << endl;++it;}
}

日常更喜欢用make_pair,因为不用声明模板参数,会自动推演。

3.3map应用举例

map为KV模型,根据Key找Value。这里举一个“查找水果次数”的例子。

查找水果次数:

示例方法一:

void test_map3()
{string strs[] = { "西瓜","苹果","西瓜","西瓜","西瓜","西瓜","苹果","樱桃" };map<string, int> countMap;for (auto& str : strs){map<string, int>::iterator ret = countMap.find(str);if (ret != countMap.end()){ret->second++;}else{countMap.insert(make_pair(str, 1));}}for (auto& e : countMap){cout << e.first << ":" << e.second << endl;}
}

3.4map的insert

依据map的应用举例,我们还要对map做进一步探索。

返回值是一个pair。

解释:

如果插入的值已经存在,则插入失败,iterator指向已经存在的元素,bool为false,

如果插入的值不存在,则插入,iterator指向新插入的元素,bool为true。

示例方法二:

void test_map3()
{string strs[] = { "西瓜","苹果","西瓜","西瓜","西瓜","西瓜","苹果","樱桃" };map<string, int> countMap;for (auto& str : strs){//如果水果不在map中,直接插入。//如果水果在map中,通过返回值拿到水果所在节点的迭代器,++次数。pair<map<string, int>::iterator, int> ret = countMap.insert(make_pair(str, 1));if (ret.second == false){ret.first->second++;}}for (auto& e : countMap){cout << e.first << ":" << e.second << endl;}
}

这里,insert充当了两个作用,一个作用是查找,一个作用是插入。

3.5map的operator[]

这里operator[]是通过调用insert来实现的。

图文分析:

operator[]的作用就是给一个key,去查找key对应的value。

为什么这里不用find实现呢?

原因:假设用find,如果map中没有这个key,如何返回?

可以用抛异常,这里先不做讲解。

说明:

这里用insert:

1、如果key不在map中,则插入pair<key_type,mapped_type()>,mapped_type()为缺省值。(调用mapped_type类型的默认构造函数构造一个对象,若是int则为0,string为空。)

2、如果key在map中,则插入失败,返回key所在节点映射对象(mapped_type)的引用。

所以map中的operator[]有三层作用:

1、插入

2、查找key对应的映射对象

3、修改key对应的映射对象

示例方法三:

void test_map3()
{string strs[] = { "西瓜","苹果","西瓜","西瓜","西瓜","西瓜","苹果","樱桃" };map<string, int> countMap;//1、如果水果在map中,则operator[]会插入pair<str,0>,返回映射对象(次数)的引用,对它进行++。//2、如果水果不在map中,则operator[]会返回映射对象(次数)的引用,对它进行++。for (auto& str : strs){countMap[str]++;}for (auto& e : countMap){cout << e.first << ":" << e.second << endl;}
}

map中,一般使用operator[]去完成:

1、插入+修改

2、修改

一般不会用他去查找,因为如果key不存在,会插入数据。

关于map的相关操作使用的接口:

1、增        insert+operator[]

2删        erase

3、查        find

4、改        operator[]

5、遍历     iterator+范围for

遍历出来的数据是按key来排序的,因为底层是搜索树,遍历走的是中序。

4.multiset和multimap

multiset和set的区别就在于multiset允许键值冗余。

void test_multi()
{multiset<int> s;s.insert(1);s.insert(2);s.insert(2);s.insert(2);s.insert(4);s.insert(3);for (auto& e : s){cout << e << endl;}
}

查找相同元素查找的是按顺序排的第一个。

multimap和map的区别也是一样允许键值冗余。

但是multimap没有operator[],因为对于相同的key,去查找value,不知道要找的是哪一个key对应的value,由于键值冗余,允许插入相同的,multimap的insert返回值也没有pair了。

5.总结

此篇主要对set和map、multiset和multimap做了简单的理解和相关应用的举例。

关于其他的接口比较简单,由于和STL中vector、list、string等接口类似,这里没有做具体讲解,只做了特别的几个接口的说明。

关于set和map更深层次的探索,请看下篇。

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

Python酷库之旅-第三方库Pandas(127)

目录 一、用法精讲 566、pandas.DataFrame.swapaxes方法 566-1、语法 566-2、参数 566-3、功能 566-4、返回值 566-5、说明 566-6、用法 566-6-1、数据准备 566-6-2、代码示例 566-6-3、结果输出 567、pandas.DataFrame.melt方法 567-1、语法 567-2、参数 567-3…

第三十篇——总结:成功的捷径是没有捷径

目录 一、背景介绍二、思路&方案三、过程1.思维导图2.文章中经典的句子理解3.学习之后对于投资市场的理解4.通过这篇文章结合我知道的东西我能想到什么&#xff1f; 四、总结五、升华 一、背景介绍 最终的总结&#xff0c;釜底抽薪&#xff0c;又一次如雷贯耳&#xff0c;…

9月26日

1.虚函数与纯虚函数&#xff1a; 在类中定义函数时&#xff0c;在函数前加关键字 virtual &#xff0c;允许在派生类中重写的方法。那么该函数就是虚函数。 纯虚函数&#xff1a;没有实现的方法&#xff0c;用于定义接口。 2.基类为什么需要虚析构函数&#xff1a; 确保删除派生…

找不到MSVCR100.dll怎么办,解决MSVCR100.dll丢失的六种方法

在计算机的日常使用中&#xff0c;我们可能会遇到各种各样的问题&#xff0c;其中之一就是MSVCR100.dll文件丢失。这个文件是Microsoft Visual C 2010的一个组件&#xff0c;如果丢失&#xff0c;可能会导致某些程序无法正常运行。那么&#xff0c;如何解决这个问题呢&#xff…

记一次Windows状态栏不显示问题

文章目录 &#x1fa9f;解决方案☁️单次处理☁️有效处理 &#x1fa9f;现象&#x1fa9f;尝试的操作⭐END&#x1f31f;跋&#x1f31f;交流方式 &#x1fa9f;解决方案 ☁️单次处理 重启explorer.exe 命令行操作 注意&#xff0c;使用命令行操作的时候&#xff0c;出现…

[嵌入式] 3588测试镜头推流拉流步骤

1. RK驱动下载 识别不出来设备&#xff0c;成砖了之后&#xff0c;在插上电源之前&#xff0c;按住boot键&#xff0c;再上电。 2. 在嵌入式设备中&#xff0c;执行命令&#xff0c;rtsp_server rtsp_server -I 1 -d /dev/video22 -w 640 -h 480 推流&#xff0c;用vlc拉流…

linux信号 | 学习信号三步走 | 全解析信号的产生方式

前言&#xff1a;本节内容是信号&#xff0c; 主要讲解的是信号的产生。信号的产生是我们学习信号的第二个阶段。 我们已经学习过第一个阶段——信号的概念与预备知识&#xff08;没有学过的友友可以查看我的前一篇文章&#xff09;。 以及我们还没有学习信号的第三个阶段——信…

【理解 Java 中的 for 循环】

理解 Java 中的 for 循环 for 循环是 Java 中用于迭代的常用控制结构&#xff0c;它可以帮助我们重复执行某段代码&#xff0c;直到满足特定条件。本文将介绍 for 循环的基本语法、执行流程、注意事项及一些练习。 基本语法 for 循环的基本语法如下&#xff1a; for (循环变…

你知道吗?制造手机芯片的关键竟然是一台“打印机”?

在我们每天离不开的智能手机里&#xff0c;藏着一颗小小的“心脏”——芯片。它虽小&#xff0c;却拥有着强大的计算能力&#xff0c;能够让我们随时随地与世界保持连接。你可能想象不到&#xff0c;制造这些精密芯片的关键设备&#xff0c;竟然与我们日常使用的打印机有着惊人…

PD快充是如何诱骗取电的

PD诱骗取电原理&#xff0c;主要指的是在使用USB Power Delivery(USB PD)协议的场景中&#xff0c;通过一种特殊设计的芯片来模拟受电设备&#xff08;如移动设备、充电宝等&#xff09;支持特定功率等级的过程。通常情况下&#xff0c;当一个支持PD协议的充电器连接到设备时…

2024年研究生数学建模“华为杯”E题——肘部法则、k-means聚类、目标检测(python)、ARIMA、逻辑回归、混淆矩阵(附:目标检测代码)

文章目录 一、情况介绍二、思路情况二、代码展示三、感受 一、情况介绍 前几天也是参加了研究生数学建模竞赛&#xff08;也就是华为杯&#xff09;&#xff0c;也是和本校的两个数学学院的朋友在网上组的队伍。昨天&#xff08;9.25&#xff09;通宵干完论文&#xff08;一条…

C语言编译和链接详解(通俗易懂,深入本质)

我们平时所说的程序,是指双击后就可以直接运行的程序,这样的程序被称为可执行程序(Executable Program)。在 Windows 下,可执行程序的后缀有.exe和.com(其中.exe比较常见);在类 UNIX 系统(Linux、Mac OS 等)下,可执行程序没有特定的后缀,系统根据文件的头部信息来判…

MyBatis<foreach>标签的用法与实践

foreach标签简介 实践 demo1 简单的一个批量更新&#xff0c;这里传入了一个List类型的集合作为参数&#xff0c;拼接到 in 的后面 &#xff0c;来实现一个简单的批量更新 <update id"updateVislxble" parameterType"java.util.List">update model…

基于LFSR和NFSR的流密码算法Grain v1

基于LFSR和NFSR的流密码算法Grain v1 0x0简介 Grain算法是由瑞典的 Hell,Johansson 和瑞士的 Meier 共同设计的一种面向硬件实现的流密码算法。Grain算法面向硬件实现&#xff0c;具有运行速度快、安全性高、灵活输出密钥流等优点&#xff0c;并已成为eSTREAM(欧洲流密码算法…

(done) 使用泰勒展开证明欧拉公式

问问神奇的 GPT&#xff0c;how to prove euler formula? 一个答案如下&#xff1a;

C++_实现日期类

✨✨ 欢迎大家来到小伞的大讲堂✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;C学习 小伞的主页&#xff1a;xiaosan_blog 1.日期类的实现接口(date.h) 对于多次调用的函数&#xff0c;我们会实现在头文件…

Vulhub TheEther_1.0.1靶机详解

项目地址 https://download.vulnhub.com/theether/theEther_1.0.1.zip实验过程 将下载好的靶机导入到VMware中&#xff0c;设置网络模式为NAT模式&#xff0c;然后开启靶机虚拟机 使用nmap进行主机发现&#xff0c;获取靶机IP地址 nmap 192.168.47.1-254根据对比可知theEthe…

【Linux 报错】vim 保存文件时出现 E45: ‘readonly‘ option is set (add ! to override)

一、错误原因 该错误表明当前你尝试保存的是一个 只读文件&#xff0c;该文件权限设置为只读&#xff0c;具有只读的标识 系统为了防止你意外修改该只读文件&#xff0c;因此会阻止对只读文件的保存&#xff08;他怕你修改了你还保存&#xff0c;破坏了只读属性&#xff09; …

实景三维夯实数字乡村孪生底座

随着数字乡村建设的不断推进&#xff0c;实景三维技术在乡村规划、管理、服务等方面发挥着越来越重要的作用。本文将探讨实景三维技术如何夯实数字乡村的孪生底座&#xff0c;为乡村的可持续发展提供强有力的支撑。 一、数字乡村建设的背景 数字乡村建设是推动乡村全面振兴、…

python实现石头,剪刀,布(turtle库简易版)

三角形为剪刀&#xff0c;红色为石头&#xff0c;圆形为布&#xff08;玩家点击&#xff09; 右边为电脑 运行截图&#xff1a; 写的比较简易&#xff0c;包括鼠标的点击&#xff08;主要想应付一下老师的作业&#xff0c;临时写的&#xff09;&#xff0c;很多都有点偏差&am…