STL之set、map的使用

STL之set、map

  • 1. 序列式容器和关联式容器
  • 2. set系列的使⽤
    • 参考文档链接:
    • 2.1 set的介绍
    • (2)set的增删查
    • 2.2 multiset的介绍
  • 3 map
    • 3.1 参考文档
    • 3.2 map类的介绍
    • 3.3 pair类型介绍
    • 3.4 map的构造
    • 3.6 map的数据修改
    • 3.7 multimap和map的差异

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

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

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

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

2. set系列的使⽤

参考文档链接:

https://legacy.cplusplus.com/reference/set/

首先我们还是先打开文档,然后我们会看到下面这个部分
在这里插入图片描述
我们可以看到set由两部分set与multiset组成,set的模型就是上一节我们写的不允许重复元素的key模型,multiset就是允许数据冗余的key模型,下面我们就来分别介绍:

2.1 set的介绍

在这里插入图片描述

• set的声明如上,T就是set底层关键字的类型(也就是上一节学的k)
• set默认要求T⽀持⼩于⽐较,如果不⽀持或者想按⾃⼰的需求⾛可以⾃⾏实现仿函数传给第⼆个模版参数
• set底层存储数据的内存是从空间配置器申请的,如果需要可以⾃⼰实现内存池,传给第三个参数。
• ⼀般情况下,我们都不需要传后两个模版参数。
• set底层是⽤红⿊树实现,增删查效率是 O(logN) ,迭代器遍历是⾛的搜索树的中序,所以是有序的。
• 前⾯部分我们已经学习了vector/list等容器的使⽤,STL容器接⼝设计,⾼度相似,所以这⾥我们就不再⼀个接⼝⼀个接⼝的介绍,⽽是直接带着⼤家看⽂档,挑⽐较重要的接⼝进⾏介绍。

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

在这里插入图片描述
这一部分我们就看一下文档里的例子就可以了,与之前的string,vector差不多。

(2)set的增删查

下面是使用代码:

void settest1()
{//set<int> st;int arr[] = { 8,1,0,7,3,9,2 };for (auto& e : arr){st.insert(e);}set<int>::iterator it = st.begin();while (it != st.end()){cout << *it << ' ';it++;}cout << endl;//普通的插入set<int> st2;st2.insert(arr, arr + sizeof(arr) / sizeof(int));for (auto& e : st2){cout << e << ' ';}cout << endl;set<int> st3(st2);for (auto& e : st3){cout << e << ' ';}cout << endl;//拷贝构造set<int> st4 = st3;for (auto& e : st4){cout << e << ' ';}cout << endl;//赋值构造set<int> st5({ 8,1,0,7,3,9,2 });//隐式类型转换for (auto& e : st5){cout << e << ' ';}cout << endl;//删除最小值st5.erase(st5.begin());for (auto& e : st5){cout << e << ' ';}cout << endl;st5.erase(--st5.end());//删除最大值for (auto& e : st5){cout << e << ' ';}cout << endl;//指定数据的删除int x = 0;cin >> x;st4.erase(x);for (auto& e : st4){cout << e << ' ';}cout << endl;//利用指定位置的迭代器来删除set中的元素cin >> x;set<int>::iterator pos = st4.find(x);st4.erase(pos);for (auto& e : st4){cout << e << ' ';}cout << endl;查找某个数据//cin >> x;//set<int>::iterator pos1 = st3.find(x);set本身提供的//set<int>::iterator pos2 = find(st3.begin(), st3.end(), x);算法库里提供的int count = st3.count(3);//这个接口原本返回的count是指这个数在set容器里的个数,set只要存在都只会是1//因为set不支持数据冗余if (st3.count(8))//在这里格外好用{cout << "存在" << endl;}else{cout << "不存在" << endl;}
}

此外在这里我介绍一下迭代器
常见的迭代器划分:
在这里插入图片描述
我们怎样来容器的迭代器类型呢?
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

迭代器的类型取决于容器的底层结构,知道迭代器的类型才能更好地帮助我们使用算法,例如,官方sort:
在这里插入图片描述
sort并不是我们随便传一个迭代器就可以使用,必须是随机迭代器,set的迭代器就是双向的,这一点大家需要知道。

我们在回归set这里:
在这里插入图片描述
set还支持最下面这三个接口,最后一个接口我这里不讲,lower_bound的接口的意思是返回大于等于我们传入的值的迭代器的接口(这个值可以不存在),upper_bound是返回大于当前传入值的迭代器(同前),那他俩有啥用呢,简单的来说这里可以完成区间的删除
例如:

void settest2()
{//区间删除set<int> st;for (int i = 1; i <= 9; i++){st.insert(i * 10);}set<int> st1(st);for (auto e : st){cout << e << ' ';}cout << endl;//这里我们的set里存着10-90的数据,现在我们需要删除30-50auto itbegin = st.lower_bound(30);//取大于等于30的区间auto itend = st.upper_bound(60);//取大于60的区间//这里的区间是左闭右开的st.erase(itbegin, itend);for (auto e : st){cout << e << ' ';}cout << endl;//假如我们10-90这几个数,我们现在要删除25-55的数字怎么办?auto itbegin1 = st1.lower_bound(25);auto itend1 = st1.upper_bound(55);st.erase(itbegin1, itend1);for (auto e : st1){cout << e << ' ';}cout << endl;
}

2.2 multiset的介绍

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

void multisettest()//允许冗余
{multiset<int> mus({ 9,0,1,3,4,4,2,7,8,9 });for (auto e : mus){cout << e << ' ';}cout << endl;//唯一的差别是如果我们要查找的值有重复,它会返回第一个在中序中出现的那个auto pos = mus.find(4);mus.erase(pos);for (auto e : mus){cout << e << ' ';}cout << endl;//count会返回存在的个数cout << "9->";cout << mus.count(9) << endl;//删除第一个9前auto pos1 = mus.find(9);mus.erase(pos1);for (auto e : mus){cout << e << ' ';}cout << endl;cout << "9->";cout << mus.count(9) << endl;//删除第一个9后
}

以上就是set的简单使用,此外set用于做题也十分好用,例如
349. 两个数组的交集 - ⼒扣(LeetCode)
142. 环形链表 II - ⼒扣(LeetCode)
这两个题目,大家自己下来去尝试去做一下。

3 map

3.1 参考文档

https://legacy.cplusplus.com/reference/map/

3.2 map类的介绍

首先它也是分为map与multimap,同样multimap允许数据冗余
在这里插入图片描述
在这里插入图片描述

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

3.3 pair类型介绍

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

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){}
};
template <class T1,class T2>
inline pair<T1,T2> make_pair (T1 x, T2 y)
{return ( pair<T1,T2>(x,y) );
}

这段代码看不懂没关系,我们只需要记住,pair里的第一个元素也就是first就是我们的key,第二个元素second就是我们的value。

3.4 map的构造

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

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

void maptest1()//map是key_value模型,比较的逻辑仍然是key
{map<int, string> mp({ {1,"mid"},{0,"left"},{2,"right"} });auto it = mp.begin();while (it != mp.end()){cout << it->first << ";" << it->second << endl;it++;}cout << endl;pair<string, int> str1("香蕉", 2);pair<string, int> str2("苹果", 1);pair<string, int> str3("菠萝", 5);map<string, int> mp1;mp1.insert(str1);mp1.insert(str2);mp1.insert(str3);auto it1 = mp1.begin();while (it1 != mp1.end()){cout << it1->first << ";" << it1->second << endl;it1++;}cout << endl;map<string, int> mp2;mp2.insert(pair < string, int>("香蕉", 2));//匿名对象mp2.insert(make_pair("苹果", 1));mp2.insert({ "菠萝",5 });//隐式类型转换这种方式日常常用一点auto it2 = mp2.begin();while (it2 != mp2.end()){cout << it2->first << ";" << it2->second << endl;it2++;}cout << endl;
}

这些就是map的构造、插入删除等接口。

3.6 map的数据修改

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

map第⼀个⽀持修改的⽅式时通过迭代器,迭代器遍历时或者find返回key所在的iterator修改,map还有⼀个⾮常重要的修改接⼝operator[],但是operator[]不仅仅⽀持修改,还⽀持插⼊数据和查找数据,所以他是⼀个多功能复合接⼝需要注意从内部实现⻆度,map这⾥把我们传统说的value值,给的是T类型,typedef mapped_type。⽽value_type是红⿊树结点中存储的pair键值对值。⽇常使⽤我们还是习惯将这⾥的T映射值叫做value。

void maptest2()
{
//	string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜",
//"苹果", "香蕉", "苹果", "香蕉" };
//
//	map<string, int> countMap;//创建map
//
//	for (const auto& str : arr)//遍历数组
//	{
//		auto ret = countMap.find(str);//如果没找到返回end迭代器,如果找到了返回当前迭代器
//		if (ret == countMap.end())//map中不存在
//		{
//			countMap.emplace(str, 1);
//		}
//		else//已经存在
//		{
//			ret->second++;
//		}
//	}
//
//	auto it = countMap.begin();
//	while (it != countMap.end())
//	{
//		cout << it->first << ";" << it->second << endl;
//		it++;
//	}
//	cout << endl;
//	//第一种方式
//
//	auto it1 = countMap.begin();
//	while (it1 != countMap.end())
//	{
//		cout << (*it1).first << ':' << (*it1).second << endl;
//		it1++;
//	}
//	cout << endl;// 利⽤[]插⼊+修改功能,巧妙实现统计⽔果出现的次数string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜","苹果", "香蕉", "苹果", "香蕉" };map<string, int> countMap;for (const auto& str : arr){// []先查找⽔果在不在map中// 1、不在,说明⽔果第⼀次出现,则插⼊{⽔果, 0},同时返回次数的引⽤,++⼀下就变成1次了// 2、在,则返回⽔果对应的次数++countMap[str]++;}for (const auto& e : countMap){cout << e.first << ":" << e.second << endl;}cout << endl;
}
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.7 multimap和map的差异

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

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

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

相关文章

openpdf

1、简介 2、示例 2.1 引入依赖 <dependency><groupId>com.github.librepdf</groupId><artifactId>openpdf</artifactId><version>1.3.34</version></dependency><dependency><groupId>com.github.librepdf</…

python+yaml+pytest+allure接口自动化框架

建议想学自动化的同学&#xff0c;先花半个月一个月的时间&#xff0c;去b站极限学习一下有关python的基础内容&#xff0c;比如各种数据类型的特点&#xff0c;创建 转换等&#xff0c;还有面向对象的一些知识&#xff0c;否则直接看自动化框架&#xff0c;很难看懂理解&#…

根据请求错误的状态码判断代理配置问题

SafeLine&#xff0c;中文名 “雷池”&#xff0c;是一款简单好用, 效果突出的 Web 应用防火墙(WAF)&#xff0c;可以保护 Web 服务不受黑客攻击。 雷池通过过滤和监控 Web 应用与互联网之间的 HTTP 流量来保护 Web 服务。可以保护 Web 服务免受 SQL 注入、XSS、 代码注入、命…

2024顶级一区idea:多模态图像融合!

在图像处理的前沿领域&#xff0c;多模态图像融合技术正成为研究的热点&#xff0c;它通过整合来自不同来源的图像数据&#xff0c;为我们提供了更丰富的信息维度&#xff0c;从而显著提升图像处理的精确度和效率。 这项技术的核心优势在于能够捕捉并融合各种图像数据中的互补…

3D渲图软件推荐:打造高质量渲染效果

在现代设计领域&#xff0c;3D渲图已经成为展示设计方案和产品外观的重要手段。无论是建筑设计、产品设计还是影视动画&#xff0c;都需要借助专业的3D渲染图软件来实现逼真的视觉效果。 本文将为您介绍几款备受好评的3D渲染图软件&#xff0c;帮助您在项目中选择合适的工具。…

户外防火值守:太阳能语音监控杆的参数及技术特点

随着假期旅游的热潮日渐高涨&#xff0c;我们游览各大景区、公园或森林区域时&#xff0c;经常会与各种智能设备不期而遇。这些高科技产品不仅提升了旅游体验&#xff0c;更在无形中保障了游客的安全与景区的环境保护。在我最近的旅行经历中&#xff0c;尤其是在深圳大鹏旅游景…

开放式蓝牙耳机排行榜10强?分享值得安利的开放式耳机

​开放式耳机目前非常流行&#xff0c;它们以时尚、美观和舒适著称&#xff0c;迅速赢得了众多用户的喜爱&#xff0c;成为了耳机市场的新宠。与传统的入耳式耳机相比&#xff0c;开放式耳机佩戴更稳固&#xff0c;对耳朵也更为温和。尽管有些人认为它们价格不菲&#xff0c;甚…

项目_C_Ncurses_Flappy bird小游戏

Ncurses库 概述 什么是Ncurses库&#xff1a; Ncurses是一个管理应用程序在字符终端显示的函数库&#xff0c;库中提供了创建窗口界面、移动光标、产生颜色、处理键盘按键等功能。 安装Ncurses库&#xff1a; sudo apt-get install libncurses5-dev 头文件与编译&#xf…

Springboot自定义starter注入到第三方项目IOC容器里

一 Bean扫描 Springboot项目&#xff0c;我们不加ComponentScan注解&#xff0c;但是也能扫描到Controller、Service标记的类&#xff0c;为什么呢&#xff1f;关键在于启动类的SpringBootApplication注解&#xff0c;该注解由以下三个注解组成&#xff1a; SpringBootConfig…

关于BSV区块链覆盖网络的常见问题解答(下篇)

​​发表时间&#xff1a;2024年9月20日 在BSV区块链上的覆盖网络服务为寻求可扩展、安全、高效交易处理解决方案的开发者和企业家开辟了新的视野。 作为开创性的曼达拉升级的一部分&#xff0c;覆盖网络服务提供了一个强大的框架&#xff0c;用于管理特定类型的交易和数据访问…

如何将 html 渲染后的节点传递给后端?

问题 现在我有一个动态的 html 节点&#xff0c;我想用 vue 渲染后&#xff0c;传递给后端保存 思路 本来想给html的&#xff0c;发现样式是个问题 在一个是打印成pdf&#xff0c;然后上传&#xff0c;这个操作就变多了 最后的思路是通过 html2canvas 转化成 canvas 然后变成…

XUbuntu安装OpenSSH远程连接服务器

目录 打开终端。更新你的包索引安装OpenSSH服务器。在终端中输入以下命令&#xff1a;安装完成后&#xff0c;OpenSSH服务器会自动启动。查看主机 IP测试连接打开 cmd 终端SSH 连接虚拟机确认连接输入连接密码发现问题修改用户&#xff0c;尝试连接 打开终端。 更新你的包索引 …

在 Android 上恢复已删除文件的 5 种简单方法

您可能会因为意外删除、未完成的 Android 更新、手机意外关机等原因而丢失 Android 上的重要数据。新技术的发展使许多手机功能或程序能够从内部恢复丢失的数据。 在 Android 上恢复已删除文件的 5 种简单方法 然而恢复成功率的不确定性也成为人们克服数据丢失困境的重要考虑因…

安卓13禁止锁屏 关闭锁屏 android13禁止锁屏 关闭锁屏

总纲 android13 rom 开发总纲说明 文章目录 1.前言2.问题分析3.代码分析4.代码修改5.彩蛋1.前言 设置 =》安全 =》屏幕锁定 =》 无。 我们通过修改系统屏幕锁定配置,来达到设置屏幕不锁屏的配置。像网上好多文章都只写了在哪里改,改什么东西,但是实际上并未写明为什么要改那…

鸿蒙NEXT开发-面试题库(最新)

注意&#xff1a;博主有个鸿蒙专栏&#xff0c;里面从上到下有关于鸿蒙next的教学文档&#xff0c;大家感兴趣可以学习下 如果大家觉得博主文章写的好的话&#xff0c;可以点下关注&#xff0c;博主会一直更新鸿蒙next相关知识 专栏地址: https://blog.csdn.net/qq_56760790/…

SQL Server 2022 RTM Cumulative Update #15 发布下载

SQL Server 2022 RTM Cumulative Update #15 发布下载 最新的累积更新 (CU) 下载&#xff0c;包含自 SQL Server 2022 RTM 发布以来的所有更新。 请访问原文链接&#xff1a;https://sysin.org/blog/sql-server-2022/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留…

物联网智能项目(含案例说明)

物联网&#xff08;Internet of Things&#xff0c;简称IoT&#xff09;智能项目是指利用物联网技术将各种物理设备、传感器、软件、网络等连接起来&#xff0c;实现设备之间的互联互通&#xff0c;并通过数据采集、传输、处理和分析&#xff0c;实现智能化管理和控制的项目。以…

ARM嵌入式学习--第二天

-指令流水线 -基础知识 1.流水线技术通过多个功能部件并行工作来缩短程序执行时间&#xff0c;提高处理器的效率和吞吐率 2.增加流水线级数&#xff0c;可以简化流水线的各级逻辑&#xff0c;进一步提高了处理器的性能 3.以三级流水线分析&#xff1a; pc代表程序计数器&#x…

如何用ChatGPT 8小时写出一篇完整论文(附完整提示词)

今天教大家如何利用ChatGPT完成一篇完整的论文。只需要一个标题&#xff0c;剩下全部由ChatGPT完成。总耗时8小时。 阅前提醒&#xff1a; 1.适用人群&#xff1a;这个方法适合应付简单的学术任务&#xff0c;比如日常小论文或投稿一般期刊。但如果你要写高水平的论文&#xf…

漏洞挖掘 | 通过错误日志实现XXE外带

介绍 在最近的一个项目中&#xff0c;我发现了一个与 XML 外部实体&#xff08;XXE&#xff09;攻击相关的重大安全问题。 本文讲述了我在项目中发现并利用 XXE 漏洞的过程&#xff0c;特别是通过一种非传统的方式——利用 Java 异常在日志文件中输出攻击结果。 什么是XXE&a…