C++STL——map和set

C++教学总目录

map和set

  • 1、set
    • 1.1、set简介
    • 1.2、set接口简介
    • 1.3、set的使用
    • 1.4、set其他接口的使用
    • 1.5、multiset
  • 2、map
    • 2.1、map简介
    • 2.2、pair使用
    • 2.3、map接口使用
    • 2.4、multimap

1、set

1.1、set简介

在这里插入图片描述

如图:set是类模板,参数T表示存储的数据类型,类似上篇的key。Compare是仿函数,因为set插入值时需要进行比较大小,这里给模板参数我们就可以自己控制。Alloc是空间配置器。set包含于头文件<set>
map和set的底层都是红黑树,红黑树是一种二叉平衡搜索树,这个我们后面会模拟实现。

在这里插入图片描述
key_type就是T,key_type就是T的重命名。这里还有一个value_type这个现在还无法解释,得后面模拟实现map和set了解底层才能解释,这里先知道即可。
在上篇我们说了key版本的二叉搜索树是不能修改值的,因为修改了key会破坏二叉搜索树的结构,那么set这里如何控制迭代器无法修改呢?

在这里插入图片描述
在这里插入图片描述

可以看到,这里的普通迭代器实际上就是const迭代器。因此无论是普通迭代器还是const迭代器都无法修改key值。


1.2、set接口简介

在这里插入图片描述
构造函数有三个分别是:无参的默认构造函数、使用迭代器区间构造、拷贝构造。

在这里插入图片描述
迭代器这边基本上和vector、list一样了,会使用其中一个容器的迭代器,其他的也都会使用。这里不再赘述。

在这里插入图片描述
这里面比较重要的函数就三个,分别是插入、删除和查找函数。其他的用的不多,可以了解一下。


在这里插入图片描述
insert函数有三个:分别是插入一个值、在某个位置插入一个值、插入一段迭代器区间。
这里的value_type实际上就是T。

在这里插入图片描述
erase函数也有三个:分别是删除某个迭代器位置的数据、删除某个值、删除一段迭代器区间。
在这里插入图片描述
find函数就是传入key进行查找,找到就返回对应位置的迭代器,找不到返回end()。


1.3、set的使用

基于二叉搜索树的性质,我们知道二叉搜索树可以排序和快速的搜索,因为中序遍历二叉搜索树就是从小到大获取数据。
下面演示使用set去重+排序:

set<int> s;
s.insert(1);
s.insert(3);
s.insert(2);
s.insert(5);
s.insert(3);
s.insert(4);set<int>::iterator it = s.begin();
while (it != s.end())
{cout << *it << " ";++it;
}
cout << endl;for (auto e : s)
{cout << e << " ";
}
cout << endl;

在这里插入图片描述
运行结果如图所示,set插入的时候类似二叉搜索树,只不过底层是红黑树需要维持高度平衡,所以多了旋转的步骤。如果树中已存在数据就不插入。使用迭代器遍历set实际上就是中序遍历。因此set可以用来排序+去重。


另外如果我们要删除数据可以这样使用:

s.erase(3);

还可以这样使用:

set<int>::iterator pos = s.find(3);
if (pos != s.end())s.erase(pos);

找不到会返回end(),所以使用find进行删除需要多一步判断。
注意:这里的find函数的时间复杂度为O(logN),如果使用算法库的find函数,时间复杂度为O(N),所以一定要使用set自带的find函数。


1.4、set其他接口的使用

在这里插入图片描述
这里我们再介绍这四个函数的使用,但是并不常用,有些主要是为了multiset设计的。


在这里插入图片描述
count函数返回树中元素的个数,由于set不能存储重复的数据,所以返回值只有1/0,因此有点鸡肋(也可以使用find)。

set<int> s;
s.insert(1);
s.insert(3);
s.insert(2);
s.insert(5);
s.insert(4);auto pos = s.find(3);
if (pos != s.end())
{cout << "找到了" << endl;
}if (s.count(3))
{cout << "找到了" << endl;
}

在这里插入图片描述
在这里插入图片描述
这两个函数是配合着使用的,lower_bound返回>=val数据的迭代器,而upper_bound返回的是>val数据的迭代器。这样就形成了一个区间,我们可以删除这个区间中的值。使用如下:

set<int> myset;
set<int>::iterator itlow, itup;
for (int i = 1; i < 10; i++) myset.insert(i * 10); // 插入10->90
// [35, 60]
itlow = myset.lower_bound(35);   // >=35
itup = myset.upper_bound(60);    // >60std::pair<std::set<int>::const_iterator, std::set<int>::const_iterator> ret;
ret = myset.equal_range(30);
std::cout << "the lower bound points to: " << *ret.first << endl;
std::cout << "the upper bound points to: " << *ret.second << endl;// [itlow, itup)
cout << *itlow << endl;
cout << *itup << endl;
myset.erase(itlow, itup);for (auto e : myset)
{cout << e << " ";
}
cout << endl;

在这里插入图片描述
上面的代码我们使用lower_bound计算出大于等于35的值,所以获取40这个数据的迭代器。使用upper_bound计算出大于60的值,所以获取70这个数据的迭代器。紧接着我们通过erase删除这段区间的值,由于迭代器区间都是左闭右开,所以实际上删除的是[40,70)之间的值,因此70并没有被删除。


在这里插入图片描述
这个函数返回一个迭代器区间。第一个值还是大于等于val的,第二个大于val。

std::set<int> myset;
for (int i = 1; i <= 5; i++) myset.insert(i * 10);   // myset: 10 20 30 40 50std::pair<std::set<int>::const_iterator, std::set<int>::const_iterator> ret;
ret = myset.equal_range(30);std::cout << "the lower bound points to: " << *ret.first << '\n';
std::cout << "the upper bound points to: " << *ret.second << '\n';

在这里插入图片描述
然后调用erase传入这个区间就可以删除区间之间的值。


1.5、multiset

在这里插入图片描述
在这里插入图片描述
multiset基本上同set,使用方式也一样,只不过multiset支持插入重复的值。因此count和equal_range在multiset这里用处更大。包含于头文件<set>

multiset<int> s;
s.insert(3);
s.insert(5);
s.insert(8);
s.insert(7);
s.insert(7);
s.insert(9);
s.insert(7);
for (auto e : s)
{cout << e << " ";
}
cout << endl;// 返回中序第一个7
auto pos = s.find(7);
while (pos != s.end())
{//*pos = 10;cout << *pos << " ";++pos;
}
cout << endl;
cout << s.count(7) << endl;auto ret = s.equal_range(6);
auto itlow = ret.first;
auto itup = ret.second;
// [itlow, itup)
// [7,7)
cout << *itlow << endl;
cout << *itup << endl;
s.erase(itlow, itup);   
for (auto e : s)
{cout << e << " ";
}
cout << endl;

在这里插入图片描述

1、这里find找出的7是中序遍历的第一个7,通过该位置继续向后遍历可以获取所有7以及大于7的值。
2、并且可以利用count函数计算出树中存储了几个7。
3、使用equal_range函数算出的两个范围分别是7,但是由于区间是[7,7)所以什么也不删除。

其他的使用同set,这里不再赘述。


2、map

2.1、map简介

在这里插入图片描述
map是key/value结构的,所以模板参数有Key、T,仿函数用来比较。包含于头文件<map>
在这里插入图片描述
map的数据类型实际上是一个pair,pair里面又有key和T,所以实际上T就是value。
map是不能修改key的,因为修改了key就会破坏二叉搜索树的结构,但是是可以修改value的。这里是通过给pair的第一个参数加上const来控制的,这样就不能修改key了。


2.2、pair使用

在这里插入图片描述
pair是一个类模板,有两个模板参数T1、T2。包含于头文件<utility>
下面给出pair的类似实现方式:

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){}
};

因此pair可以存储两个类型,可以是同种类型,也可以是不同类型。
例如做算法题我们需要存储点的位置,可以使用下面的方式来存储:

typedef pair<int, int> PII;
const int N = 100010;
PII sec[N];
sec[0] = {1, 1};
//.....

另外上面的equal_range函数的返回值也是一个pair,是同种类型的迭代器,由于pair是通过struct定义出来的,所以可以直接在类外访问成员变量,即通过:pair.first和pair.second来访问。


2.3、map接口使用

在这里插入图片描述
构造函数分别为:无参的默认构造函数、使用迭代器区间构造、拷贝构造函数。
在这里插入图片描述
map同样有find、count、lower_bound、upper_bound、equal_range。而这六个函数在set我们已经介绍过了,这里不再赘述。


在这里插入图片描述
insert函数有三个:插入一个pair(这里的value_type就是pair)、在某个位置插入一个pair、插入一段迭代器区间。
下面来看insert的使用:

map<string, string> dict;
pair<string, string> kv1("insert", "插入");
dict.insert(kv1);

这样写会比较麻烦,还可以我们直接构造一个临时对象然后这个临时对象再去拷贝构造,编译器会优化成一个构造函数:

dict.insert(pair<string, string>("insert", "插入"));

但是这么写还是很麻烦,这里就需要使用一个函数了:make_pair
在这里插入图片描述
make_pair是pair类内提供的一个函数,这个函数直接返回一个临时对象,所以我们可以直接使用make_pair来构造,等价于上面的写法,只不过这样写更加简便:

dict.insert(make_pair("insert", "插入"));

但是这种写法还不是最简便的写法,C++11支持多参数的构造函数隐式类型转换,还可以这么写:

dict.insert({ "insert", "插入" });

而我们之前的一些写法是支持了单参数的构造函数隐式类型转换,如下:

string str = "hello";

上面的代码发生了隐式类型转换,先用字符串构造一个临时的string对象,再用这个临时的string对象去拷贝构造str,连续的构造+拷贝构造编译器优化成直接用字符串构造str对象。

向dict中插入几个pair对象并使用迭代器遍历:

map<string, string> dict;
dict.insert(make_pair("string", "字符串"));
dict.insert(make_pair("sort", "排序"));
dict.insert(make_pair("insert", "插入"));auto it = dict.begin();
while (it != dict.end())
{cout << (*it).first << ":" << (*it).second << endl;++it;
}

在这里插入图片描述
迭代器解引用后获取的是pair对象,所以还需要.first和.second访问成员变量。另外还可以使用->来访问更简便:

cout << it->first << ":" << it->second << endl;

这是因为迭代器重载了operator->,返回值是pair*的指针,所以正确写法应该是it->->first,只不过为了观赏性省略掉一个->。支持迭代器都可以使用范围for,这里不再演示。


在这里插入图片描述
erase函数支持删除迭代器位置的数据,传入key删除数据,删除迭代器区间。


我们注意到:map和set相比,多了operator[]这个函数:
在这里插入图片描述
前面讲了key_type就是key,mapped_type就是value。所以这个函数参数就是传入key,然后返回value的引用,因此使用这个函数可以修改value的值。

(*((this->insert(make_pair(k,mapped_type()))).first)).second

这个函数的实现调用了insert函数。
并且注意到insert的返回值是pair<iterator, bool>
在这里插入图片描述
在operator[]中调用insert函数的返回值:
1、key已经在树里面,返回pair<树里面key所在节点的iterator,false>
2、key不在树里面,返回pair<新插入key所在节点的iterator,true>
分析这行代码:
在这里插入图片描述
用map来统计次数:

string arr[] = { "西瓜", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };
map<string, int> countMap;
for (auto e : arr)
{auto it = countMap.find(e);if (it == countMap.end()){countMap.insert(make_pair(e, 1));}else{it->second++;}
}
for (const auto& e : countMap)
{cout << e.first << ":" << e.second << endl;
}

重载了operator[],我们还能这么写:

string arr[] = { "西瓜", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };
map<string, int> countMap;
for (auto e : arr)
{countMap[e]++;
}
for (const auto& e : countMap)
{cout << e.first << ":" << e.second << endl;
}

如果水果没有在树里面,那就插入并返回value的引用,由于构造piar时使用的是默认构造,所以返回的value是0,然后对0进行++。
如果水果在树里面,那insert就插入失败返回,operator[]中返回value的引用,然后对value的值进行++。

operator[]中调用了insert,不管key在树中是否存在(不在就插入),都会将key位置的迭代器构造一个pair返回,通过pair.first可以获取到key位置的迭代器,通过pair.second的值true/false可以判断是否插入成功。


map<string, string> dict;
dict.insert(make_pair("string", "字符串"));
dict.insert(make_pair("sort", "排序"));
dict.insert(make_pair("insert", "插入"));cout << dict["sort"] << endl; // 查找和读
dict["map"];                  // 插入
dict["map"] = "映射,地图";     // 修改
dict["insert"] = "xxx";       // 修改
dict["set"] = "集合";         // 插入+修改

最后一个先调用insert插入字符串"set",然后返回值value的引用是空串,紧接将value修改为"集合"。

2.4、multimap

在这里插入图片描述
在这里插入图片描述
multimap支持插入重复key的pair键值对。使用方式类似set和multiset之间的区别。

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

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

相关文章

【Research Proposal】基于提示词方法的智能体工具调用研究——研究问题

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: AIGC | ChatGPT 文章目录 &#x1f4af;前言&#x1f4af;研究问题1. 如何优化提示词方法以提高智能体的工具调用能力&#xff1f;2. 如何解决提示词方法在多模态任务中的挑战&#xff1f;3. 如何通过提示词优化智能体…

PLC数据采集网关(三格电子)

产品概述 PLC转Modbus网关型号SG-PLC-Private&#xff08;PLC私有协议网关&#xff09;&#xff0c;是三格电子推出的工业级网关&#xff08;以下简称网关&#xff09;&#xff0c;主要用于在不需要对PLC编程的情况下将PLC数据映射到Modbus TCP(映射的方式符合PLC工程师使用习惯…

【HBase】HBaseJMX 接口监控信息实现钉钉告警

目录 一、JMX 简介 二、JMX监控信息钉钉告警实现 一、JMX 简介 官网&#xff1a;Apache HBase ™ Reference Guide JMX &#xff08;Java管理扩展&#xff09;提供了内置的工具&#xff0c;使您能够监视和管理Java VM。要启用远程系统的监视和管理&#xff0c;需要在启动Java…

Qt开发⑥Qt常用控件_下_多元素控件+容器类控件+布局管理器

目录 1. 多元素控件 1.1 ?Widget 和 ?View 之间的区别 1.2 List Widget 纵向列表 1.3 Table Widget 表格 1.4 Tree Widget 树形控件 2. 容器类控件 2.1 Group Box 分组框 2.2 Tab Widget 标签页控件 3. 布局管理器 3.1 垂直布局QVBoxLayout 3.2 水平布局QHBoxLayo…

科普mfc100.dll丢失怎么办?有没有简单的方法修复mfc100.dll文件

当电脑频繁弹窗提示“mfc100.dll丢失”或应用程序突然闪退时&#xff0c;这个看似普通的系统文件已成为影响用户体验的核心痛点。作为微软基础类库&#xff08;MFC&#xff09;的核心组件&#xff0c;mfc100.dll直接关联着Visual Studio 2010开发的大量软件运行命脉。从工业设计…

并行计算考前复习整理

并行计算考前复习整理 &#xff08;lwg老师会在最后一节课跟大家讲考点&#xff0c;考试考的东西不会在考点之外&#xff0c;这里面我整理的内容已经将考点全部囊括&#xff0c;最终100分&#xff09; 一、向量求和函数 C语言的串行化实现 CUDA的并行化实现 1、问题一&am…

Windows - 通过ssh打开带有图形界面的程序 - 一种通过计划任务的曲折实现方式

Windows(奇思妙想) - 通过ssh打开带有图形界面的程序 - 一种通过计划任务的曲折实现方式 前言 Windows启用OpenSSH客户端后就可以通过SSH的方式访问Windows了。但是通过SSH启动的程序&#xff1a; 无法显示图形界面会随着SSH进程的结束而结束 于是想到了一种通过执行“计划…

[C#]C# winform部署yolov12目标检测的onnx模型

yolov12官方框架&#xff1a;github.com/sunsmarterjie/yolov12 【测试环境】 vs2019 netframework4.7.2 opencvsharp4.8.0 onnxruntime1.16.3 【效果展示】 【调用代码】 using System; using System.Collections.Generic; using System.ComponentModel; using System.…

51单片机-按键

1、独立按键 1.1、按键介绍 轻触开关是一种电子开关&#xff0c;使用时&#xff0c;轻轻按开关按钮就可使开关接通&#xff0c;当松开手时&#xff0c;开关断开。 1.2、独立按键原理 按键在闭合和断开时&#xff0c;触点会存在抖动现象。P2\P3\P1都是准双向IO口&#xff0c;…

Baklib云智协同:数字资产赋能企业效能跃升

内容概要 在数字化转型加速的背景下&#xff0c;Baklib通过构建智能化的知识中台架构&#xff0c;为企业打造了贯穿知识采集、整合、应用的全链路解决方案。该平台以动态知识图谱为核心技术底座&#xff0c;支持文档、音视频、代码等20余种格式的数字资产全生命周期管理&#…

Windows10配置C++版本的Kafka,并进行发布和订阅测试

配置的环境为&#xff1a;Release x64下的环境 完整项目&#xff1a;https://gitee.com/jiajingong/kafka-publisher 1、首先下载相应的库文件&#xff08;.lib&#xff0c;.dll&#xff09; 参考链接&#xff1a; GitHub - eStreamSoftware/delphi-kafka GitHub - cloade…

基于云的物联网系统用于实时有害藻华监测:通过MQTT和REST API无缝集成ThingsBoard

论文标题 **英文标题&#xff1a;**Cloud-Based IoT System for Real-Time Harmful Algal Bloom Monitoring: Seamless ThingsBoard Integration via MQTT and REST API **中文标题&#xff1a;**基于云的物联网系统用于实时有害藻华监测&#xff1a;通过MQTT和REST API无缝集…

构建医疗Mini DeepSeek R1:用强化学习训练

构建 医疗迷你 DeepSeek R1&#xff1a;用强化学习训练 在当今快速发展的技术时代&#xff0c;大语言模型&#xff08;LLMs&#xff09;与医疗的结合带来了无限的机遇和独特的挑战。本文探索如何利用 Group Relative Policy Optimization&#xff08;GRPO&#xff09;——由 D…

在mfc中使用自定义三维向量类和计算多个三维向量的平均值

先添加一个普通类, Vector3.h, // Vector3.h: interface for the Vector3 class. // //#if !defined(AFX_VECTOR3_H__53D34D26_95FF_4377_BD54_57F4271918A4__INCLUDED_) #define AFX_VECTOR3_H__53D34D26_95FF_4377_BD54_57F4271918A4__INCLUDED_#if _MSC_VER > 1000 #p…

DeepSeek、微信、硅基流动、纳米搜索、秘塔搜索……十种不同方法实现DeepSeek使用自由

为了让大家实现 DeepSeek 使用自由&#xff0c;今天分享 10 个畅用 DeepSeek 的平台。 一、官方满血版&#xff1a;DeepSeek官网与APP 首推&#xff0c;肯定是 DeepSeek 的官网和 APP&#xff0c;可以使用满血版 R1 和 V3 模型&#xff0c;以及联网功能。 网址&#xff1a; htt…

Solon Cloud —— 介绍

说明 前面的章节&#xff0c;我们讲解了 Solon 的开发应用&#xff0c;接下来准备讲解 Solon Cloud 的的开发。Solon Cloud 是为微服务和云原生准备的分布式开发套件。 微服务 就像 MVC 一样&#xff0c;对于微服务的理解也是有不同的。微服务是一组协调工作的小而自治的服务…

python中的异常-模块-包

文章目录 异常异常的定义异常捕获语法捕获常规异常捕获指定异常捕获多个异常捕获所有异常异常else异常finally 异常传递总结 模块概念导入自定义模块及导入main方法all变量 总结 包自定义包定义pycharm中建包的基本步骤导入方式 第三方包 异常 异常的定义 当检测到一个错误时…

跟着柳叶刀数字健康,学习如何通过病理切片预测分子分类对预后的影响|项目复现

小罗碎碎念 项目复现 今天和大家分享一个非常具有参考价值的项目,手把手带着大家复现一篇发表在柳叶刀数字健康的文章。 花了六个小时才完成的这篇推送,信息量非常大,遇到了很多报错问题,但是解决以后的感觉是非常爽的,先给大家展示一下最终的成果——在同一张切片上,通…

Android Http-server 本地 web 服务

时间&#xff1a;2025年2月16日 地点&#xff1a;深圳.前海湾 需求 我们都知道 webview 可加载 URI&#xff0c;他有自己的协议 scheme&#xff1a; content:// 标识数据由 Content Provider 管理file:// 本地文件 http:// 网络资源 特别的&#xff0c;如果你想直接…

PyTorch 源码学习:阅读经验 代码结构

分享自己在学习 PyTorch 源码时阅读过的资料。本文重点关注阅读 PyTorch 源码的经验和 PyTorch 的代码结构。因为 PyTorch 不同版本的源码实现有所不同&#xff0c;所以笔者在整理资料时尽可能按版本号升序&#xff0c;版本号见标题前[]。最新版本的源码实现还请查看 PyTorch 仓…