【C++】认识map和set

目录

前言:

一:认识map和set

二:map和set的使用

1.set的使用

2.map的使用 

三:map的insert方法返回值

四:map的[ ]的使用

五:multiset和multimap

六:map和set的底层数据结构

七:map和set的迭代器

总结:


前言:

我们通过之前的学习,已经学会了AVL树和红黑树,但是STL中并没有这两种类,它是对红黑树进行了封装,供上层使用的。

这里,我们就要先学会使用map和set了,是STL中的类模板,它们的底层都是红黑树。

一:认识map和set

大家有没有想过,生活中很多东西都是一对一的。比如去停车场停车时,收费站会先记录车牌号,之后一个车牌号对应一个存放时间;或者通讯录中一个号码对应一个联系人等。这种方法就是键值对。一个唯一的键,对应一个唯一的值。但是你可以发现,这个值是可以修改的,而键不能修改。

比如一个老师要对每个学生管理,就要把每个学生学号管理起来,此时没有用到值,只有键。

通过以上两个例子,你就会发现,键都是唯一的,而值不是。这里再次说明,键也是不能修改的。

map中就是使用键值对的方式存储;而set中只有键。

map中的值能修改,键不能修改;set中只有键,不能修改。

二:map和set的使用

既然我们知道了map存储的是键值对,set存储的是键,那么接下来,我们就要使用它们了。

1.set的使用

这个很简单,引入<set>头文件,之后可以去观察cplusplus官网查看方法的使用(这里不是作者偷懒,而是我们到了现在这个阶段,就必须学会看文档了)。以下给出使用方法:

int main()
{set<int> s;s.insert(4);s.insert(6);s.insert(3);s.insert(5);set<int>::iterator it = s.begin();//auto it = s.begin();//底层是二叉搜索树 默认中序遍历while (it != s.end()){cout << *it << " ";++it;}cout << endl;//删除最小值 也就是最左边的迭代器指向的位置s.erase(s.begin());  //通过返回值判断是否删除成功int x;cin >> x;//int num = s.erase(x);//if (num == 0)//{//	cout << x << "不存在!" << endl;//}for (auto e : s){cout << e << " ";}cout << endl;set<int>::iterator pos = s.find(x);if (pos != s.end()){s.erase(pos);}else{cout << x << "不存在!" << endl;}for (auto e : s){cout << e << " ";}cout << endl;//使用算法头文件的find查找auto pos1 = find(s.begin(), s.end(), x); //O(N) auto pos2 = s.find(x);                   //O(logN)//用find查找元素是否存在并不方便 使用count方便cin >> x;if (s.count(x)){cout << x << "在!" << endl;}else{cout << x << "不存在!" << endl;}return 0;
}

对没错,就这么多。

2.map的使用 

map存储的是键值对,那么当然要有两个模板参数,但是我们如何插入数据呢?

在此之前我们先来认识一个pair类:

可以发现它是一个结构体,两个模板参数。

成员变量为:

对,很简单,map就是存的它,但是对first加上了const修饰。接下来我们看看map的使用方法:

int main()
{map<string, string> dict;//我们要使用一个类向map中插入数据pair<string, string> kv1("left", "左边");dict.insert(kv1);//传入匿名对象dict.insert(pair<string, string>("right", "右边"));//还有一个函数模板 make_pair 只用传入键值对 就可以返回一个pair对象dict.insert(make_pair("insert", "插入"));//还有我们可以传入多参数的初始化对象的方法dict.insert({ "string", "字符串" });map<string, string> dict1 = { {"left", "左边"}, {"right", "右边"} };map<string, string>::iterator it = dict.begin();while (it != dict.end()){//*it返回的是pair 但是其不支持流提取//cout << (*it) << endl;//可以去看pair 两个成员变量 first 和 second//这里不是插入顺序 而是字符ASCII码排序 中序遍历//cout << (*it).first << ":" << (*it).second << endl;cout << it->first << ":" << it->second << endl; //最好用第二种++it;}cout << endl; //用自定义类型写范围for遍历map 最好这样写 不用深拷贝for (const auto& e : dict){cout << e.first << ":" << e.second << endl;}cout << endl;string arr[] = { "苹果", "西瓜","苹果","西瓜","苹果" };map<string, int> Count;int tmp = 1;for (const auto& str : arr){//find返回的是迭代器auto ret = Count.find(str);//找到返回那个未知的迭代器 没有找到返回endif (ret == Count.end()){//没有这个元素Count.insert({str, 1});}else{++ret->second;}}auto it1 = Count.begin();while (it1 != Count.end()){//*it返回的是pair 但是其不支持流提取//cout << (*it) << endl;//可以去看pair 两个成员变量 first 和 second//这里不是插入顺序 而是字符ASCII码排序 中序遍历//cout << (*it).first << ":" << (*it).second << endl;cout << it1->first << ":" << it1->second << endl; //最好用第二种++it1;}return 0;
}

以上的insert方法我们需要注意,还有迭代器的使用方法,运行结果为:

三:map的insert方法返回值

这里我们最需要注意的就是map的insert方法返回值:

返回的是一个pair类,第一个参数是iterator,第二个参数是bool。

这是什么意思呢? 这里先说明第二个参数bool:当我们插入一个键时,如果当前键存在,则说明此键已经不能再次插入了,也就是插入失败,所以测试第二个参数为false;当前键不存在,返回true。

第二个参数是iterator,也就是迭代器,经过我们之前的学习(无中生有哈哈),已经知道了iterator本就是指针,这个指针是什么类型的呢?对,是map中存储数据节点的类型。你已经知道map中存储的是pair,那么这个迭代器本质也就是pair的指针。

所以为了测试insert方法,我们需要拿iterator来接收它,因为insert返回的是pair,且第一个参数类型是iterator所以要像下面这样写:

int main()
{map<int, int> m;int a[] = { 1, 2, 3 };for (auto e : a){m.insert({ e, e });}map<int, int>::iterator it;it = m.insert({ 10, 1 }).first; //insert不能修改!cout << it->first << endl;cout << it->second << endl;return 0;
}

上面代码中我们插入了一个不存在的键,此时insert方法返回的就是{ iterator, true },其中iterator是插入10位置的指针,true代表插入成功。所以结果如下:

之后我们再来尝试插入相同的值,再次插入10这个键:

int main()
{map<int, int> m;int a[] = { 1, 2, 3 };for (auto e : a){m.insert({ e, e });}map<int, int>::iterator it;it = m.insert({ 10, 1 }).first; //insert不能修改!//再次插入10 观察返回的pair中的值//因为iterator是指针 所以使用重载的->cout << "iterator指向的节点为:" << m.insert({10, 3}).first->first << endl;if (m.insert({ 10, 3 }).second)cout << "插入成功" << endl;elsecout << "插入失败" << endl;cout << "此时10对应的值为:" << m.insert({10, 3}).first->second << endl;return 0;
}

运行结果为:

可以看到,当10再次插入,已经无法插入,但是返回的iterator会指向10,所以对应的值也无法修改,且insert返回pair第二个参数为false;第一次插入返回10的位置,值为true。

那么我们如何改变对应的值呢?刚才不是说能修改对应的值吗?

四:map的[ ]的使用

这时我们要修改值不能使用insert,而要是用 [ ] ,这个 [ ] 充当着插入,修改,查找的功能(也就是说底层封装的insert,更加详细的解释可以先下一篇用红黑树实现map和set)。

int main()
{map<string, string> dict;dict.insert(make_pair("sort", "排序"));//插入 + 修改dict["left"] = "左边";//修改dict["left"] = "左边、剩余";//key不存在->插入 <"insert", "">dict["insert"];//key存在  -> 查找cout << dict["left"] << endl;  //使用[]要谨慎return 0;
}

五:multiset和multimap

这两个和set、map唯一区别就是他们能存相同的键,但使用用法和set、map一样,不赘述,看文档。

六:map和set的底层数据结构

前面说了这么多,它们的底层数据结构到底是什么?其实就是红黑树!所以不能有相同的键。

七:map和set的迭代器

这个很重要,我们已经知道了底层是红黑树,那么迭代器起始位置就是中序遍历的第一个节点。所以当我们使用迭代器遍历的时候顺序是键的升序排序。

而刚才说的multi家族,去找一个键的时候,返回的是中序遍历找到第一个节点所在位置。

总结:

我承认这篇写的很水,因为使用真的很简单,相信大家也能克服难关。不过大家重点要看insert方法和重载 [ ] ,因为我们接下来就要实现它,大型连续剧之——map和set的实现为您播出!敬请期待!

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

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

相关文章

Mybatis中的一级二级缓存扫盲

思维导图&#xff1a; MyBatis 提供了一级缓存和二级缓存机制&#xff0c;用于提高数据库查询的性能&#xff0c;减少对数据库的访问次数。&#xff08;本质上是减少IO次数&#xff09;。 一级缓存 1. 概念 一级缓存也称为会话缓存&#xff0c;它是基于 SqlSession 的缓存。在同…

uniapp 实现低功耗蓝牙连接并读写数据实战指南

在物联网应用场景中&#xff0c;低功耗蓝牙&#xff08;BLE&#xff09;凭借其低能耗、连接便捷的特点&#xff0c;成为设备间数据交互的重要方式。Uniapp 作为一款跨平台开发框架&#xff0c;提供了丰富的 API 支持&#xff0c;使得在多个端实现低功耗蓝牙功能变得轻松高效。本…

OpenSSL应用实践:嵌入式数据安全实战指南

文章目录 OpenSSL应用实践:嵌入式数据安全实战指南一、嵌入式安全现状与OpenSSL适配方案1.1 嵌入式安全挑战1.2 OpenSSL精简方案二、开发环境搭建2.1 交叉编译工具链2.2 OpenSSL交叉编译三、核心功能实现3.1 AES-GCM加密实践四、实战项目:安全OTA升级4.1 系统架构4.2 关键代码…

harmonyOS 手机,双折叠,平板,PC端屏幕适配

由于HarmonyOS设备的屏幕尺寸和分辨率各不相同&#xff0c;开发者需要采取适当的措施来适配不同的屏幕。 1.EntryAbility.ets文件里&#xff1a;onWindowStageCreate方法里判断设备类型&#xff0c; 如果是pad&#xff0c;需全屏展示&#xff08;按客户需求来&#xff0c;本次…

跟韩学AiOps系列之2025学MySQL系列_如何在MySQL中开启和提交事务?!

跟韩学AiOps系列之2025学MySQL系列_如何在MySQL中开启和提交事务&#xff1f;! 文章目录 一、事务的基本操作1. 开启事务2. 执行事务内操作3. 提交事务4. 回滚事务 二、验证示例&#xff08;适用于 MySQL 5.7&#xff09;步骤 1&#xff1a;准备测试表和数据步骤 2&#xff1a…

Java生成微信小程序码及小程序短链接

使用wx-java-miniapp-spring-boot-starter 生成微信小程序码及小程序短链接 在pom.xml文件中引入依赖 <dependency><groupId>com.github.binarywang</groupId><artifactId>wx-java-miniapp-spring-boot-starter</artifactId><version>4.7…

如何让通义千问大模型支持结构化输出?

之前的文章提到通义千问API无法通过with_structured_output/json schema的方式支持结构化输出&#xff0c;如果就是想使用通义千问大模型做结构化输出&#xff0c;应该怎么办呢&#xff1f;有两种办法 使用Ollama来运行通义千问大模型 从Ollama博客文章 Structured output 中…

一条 SQL 查询语句是如何执行的(MySQL)

第一讲&#xff1a;一条 SQL 查询语句是如何执行的 总览图示 MySQL 查询的执行流程可以大致分为以下步骤&#xff08;如图所示&#xff09;&#xff1a; 连接器&#xff08;Connection&#xff09;查询缓存&#xff08;Query Cache&#xff0c;MySQL 8.0 已废弃&#xff09;…

汽车OTA在线升级法规分析

摘要 本文介绍了R156法规即《关于批准车辆的软件升级和软件升级管理体系统一规定的法规》、该法规专注于汽车软件升级功能&#xff0c;并为此提出了一系列具体要求&#xff0c;旨在确保软件升级流程的安全性、可控性和合规性&#xff0c;从而顺应汽车行业智能化、联网化的发展趋…

Notepad编辑器实现换行符替换

在不同的Note编辑器中&#xff0c;批量把换行替换为空的方法有所不同&#xff0c;以下是常见编辑器的操作方法&#xff1a; Notepad 打开文件后&#xff0c;按CtrlH打开“查找和替换”对话框&#xff0c;在“查找”字段中输入\r\n&#xff0c;在“替换为”字段中输入一个空格…

Rust多线程性能优化:打破Arc+锁的瓶颈,效率提升10倍

一、引言 在 Rust 开发中&#xff0c;多线程编程是提升程序性能的重要手段。Arc&#xff08;原子引用计数&#xff09;和锁的组合是实现多线程数据共享的常见方式。然而&#xff0c;很多程序员在使用 Arc 和锁时会遇到性能瓶颈&#xff0c;导致程序运行效率低下。本文将深入剖…

【安装指南】Centos7 在 Docker 上安装 RabbitMQ4.0.x

目录 前置知识:RabbitMQ 的介绍 一、单机安装 RabbitMQ 4.0.7版本 1.1 在线拉取镜像 二、延迟插件的安装 2.1 安装延迟插件 步骤一:下载延迟插件 步骤二:将延迟插件放到插件目录 步骤三:启动延迟插件 步骤四:重启 RabbitMQ 服务 步骤五:验收成果 步骤六:手动…

【quantity】5 derive_more库 2.0 版介绍

derive_more 是一个 Rust 过程宏库&#xff0c;旨在通过派生宏自动生成常见 trait 的实现&#xff0c;减少样板代码。2.0 版本带来了多项改进和新特性。 主要特性 1. 支持的 Trait 派生 derive_more 2.0 支持派生以下 trait&#xff1a; 基本操作 trait: Display - 格式化显…

网站备份,网站数据备份的步骤

网站备份&#xff08;尤其是网站数据备份&#xff09;是保障业务连续性、防止数据丢失和应对安全威胁的关键措施。以下是系统化的备份步骤和实施建议&#xff0c;涵盖技术操作、策略规划及常见问题处理&#xff1a; 一、备份前的准备工作 明确备份范围 核心数据&#xff1a;…

OpenCV 图形API(72)图像与通道拼接函数-----根据指定的方式翻转图像(GMat)函数 flip()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 翻转一个2D矩阵&#xff0c;围绕垂直轴、水平轴或同时围绕两个轴。 该函数以三种不同的方式之一翻转矩阵&#xff08;行和列的索引是从0开始的&a…

医生视角下转录组学的生物信息学分析

医生视角下转录组学的生物信息学分析 转录组学的生物信息学分析是医生解决临床与科研问题的有力工具。这里罗列医学转录组学相关的几个概念&#xff0c;从使用者&#xff08;医生&#xff09;的角度看待理解相关技术&#xff0c;为后续使用该技术说明临床和科研问题奠定基础。…

量子机器学习中的GPU加速实践:基于CUDA Quantum的混合编程模型探索

引言&#xff1a;量子机器学习的新范式 在量子计算与经典机器学习交叉融合的前沿领域&#xff0c;量子机器学习&#xff08;Quantum Machine Learning, QML&#xff09;正经历着革命性突破。然而&#xff0c;随着量子比特规模的增长和算法复杂度的提升&#xff0c;传统计算架构…

Matplotlib核心课程-2

4.1 数据加载、储存 4.1.1 从数据文件读取数据 导入支持库&#xff1a; import numpy as np from pandas import Series,DataFrame import pandas as pd 从csv文件读取数据&#xff0c;一般方法&#xff1a; pd.read_csv(../data/ex1.csv,encodinggbk) 从csv文件读取数据&#…

new和malloc的区别

1 语义层级不同&#xff1a;语言机制 vs. 库函数 new / new[] (C 关键字)malloc / calloc / realloc (C 运行时函数)本质语言级运算符&#xff1b;可被重载库函数&#xff1b;无法重载作用分配内存 并调用构造函数仅分配原始字节块&#xff0c;不做初始化&#xff0c;也不调用…

C++11新特性_自动类型推导_auto

在 C11 标准中&#xff0c;auto关键字被赋予了全新且强大的功能&#xff0c;它用于自动类型推导&#xff0c;即编译器能够根据变量的初始化表达式自动确定其类型。 基本语法 使用auto声明变量时&#xff0c;只需给出auto关键字&#xff0c;后面紧跟变量名&#xff0c;并对其进…