【C++】STL——set/multiset 和 map/multimap的使用

文章目录

  • 1. 关联式容器
  • 2. 树形结构的关联式容器
  • 3. set
    • 3.1 认识set
    • 3.1 set的使用
  • 4. multiset
  • 5. map
    • 5.1 认识map
    • 5.2 pair
    • 5.3 map的使用
      • 对map中[]的理解
  • 6. multimap

1. 关联式容器

在初阶阶段,我们已经接触过STL中的部分容器

在这里插入图片描述
比如:vector、list、deque、forward_list(C++11)等,这些容器统称为序列式容器,因为其底层为线性序列的数据结构,里面存储的是元素本身。

而今天我们要学习的几个容器称为关联式容器,那什么是关联式容器?它与序列式容器有什么区别?

关联式容器也是用来存储数据的,与序列式容器不同的是,其里面存储的是<key, value>结构的键值对,在数据检索时比序列式容器效率更高。
在这里插入图片描述

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

根据应用场景的不同,STL总共实现了两种不同结构的关联式容器:树型结构与哈希结构。

树型结构的关联式容器主要有四种:

map、set、multimap、multiset。
这四种容器的共同点是:使用平衡二叉搜索树(即红黑树)作为其底层结构,容器中的元素是一个有序的序列。
关于平衡二叉树我们后面也会讲到,其实我们在前面几篇文章讲解搜索二叉树的时候也提到过,普通的搜索二叉树如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。
那平衡二叉树顾名思义就是在普通搜索二叉树的基础上让它变的平衡(需要对树中的结点进行调整)。
这个我们后面讲到再细说。

下面一依次介绍每一个容器。

3. set

3.1 认识set

首先我们来看一下set
在这里插入图片描述

还是一个类模板,有三个模板参数,但其实平时我们使用的时候一般只需要管第一个模板参数就行了。
因为后两个都是缺省值的,当然如果你想改变它底层搜索树的排序方式你可以自己传第二个比较方式的仿函数(默认是升序)。

关于set的仔细介绍大家可以去看文档
在这里插入图片描述
不过是英文的,大家可以借助翻译工具查看
在这里插入图片描述

3.1 set的使用

由于我们之前已经学了好几个STL里面的容器,所以这里对于这些容器的使用,其实对我们应该是比较轻松的。所以我这里就不会像之前那样特别仔细去一个个介绍它的接口了,很多东西相信到现在这个阶段大家看着文档都能搞懂
在这里插入图片描述
它提供的接口大家可以先看一下,当然其中一些都不常用。

那我们上面说到:

这几个关联式容器它们的底层结构其实就是我们之前学的二叉搜索树,只不过是平衡搜索树罢了。
那set其实就对应我们前面学的搜索二叉树的应用里面的K模型,下面要学的map就对应KV模型。

那我们接下来就来熟悉一下它的使用:

看一下它的构造函数

在这里插入图片描述
那我们来构造一个空的set,然后插入一些值
首先使用set要包含对于的头文件#include <set>
在这里插入图片描述
我们使用insert插入几个元素(这里我们看他的接口其实就没有push啥的了,一般线性的序列式容器才会有push)。

那然后我想打印出来看看,那我们就可以使用迭代器去遍历它

在这里插入图片描述
但是我们看到这里打印出来是1,2,3,4。
但是我们插入了好几个值啊,并且是无序的,那这里怎么回事呢?
🆗,上面说了它的底层是平衡二叉树,那我们学过二叉搜索树其实就明白怎么回事了:
搜索二叉树一般是不能有重复值的,所以如果多次插入相同的值,后面的就会插入失败(或者可以理解成它会自动进行去重);另外我们知道二叉搜索树也叫做二叉排序树,中序遍历就可以得到一个升序序列,所以这里默认遍历打印出来就是有序了。
所以可以认为set可以实现排序+去重
那支持迭代器的话,我们就可以用范围for。
在这里插入图片描述

那大家思考一下我们可以修改它里面的值吗?

在这里插入图片描述
当然是不行的,因为我们说了它的底层是搜索树,那搜索树如果我们可以任意修改里面的值的话,修改之后还能保证它还是一棵搜索树吗?
就不一定了,因为搜索树是需要满足对于的大小规则的。
所以不能修改。

如果我们想判断一个元素在不在set对象里面

可以用find接口
在这里插入图片描述
在这里插入图片描述
但其实还可以用另一个接口——count
在这里插入图片描述
count是统计元素的个数,但是我们的set里面不允许重复值,所以结果只有1和0,存在就是1,不存在就是0
在这里插入图片描述

那剩下的其它的接口,我们这里就不介绍了,有了前面的基础,相信大家一看就会,还有的就是不怎么用的,或者现阶段我们不是很能看懂的。

4. multiset

那在<set>这个头文件里面呢,出来set,还有一个multiset

在这里插入图片描述
multiset叫做多重集合,multi有多种的意思嘛。
在这里插入图片描述
还是这些接口。

那它跟set有什么区别呢?

🆗,它跟set最大的区别就是允许里面存的key值冗余。
我们可以来看一下
在这里插入图片描述
可以认为它有排序,但没进行去重
那允许键值冗余的话它的插入怎么做呢?
那就接着插就行了,即使插入的是已经存在的值,那搜索树的话我们说大的值要插入到右子树,小的插入到左子树,那现在相等的话其实插入到哪边都可以,可以左边也可以右边,反正平衡二叉树嘛,插入之后会对结点进行调整使得两边平衡(我们后面会讲平衡二叉树)。

然后想给大家说一下find:

现在允许键值冗余了,那就有一个问题:
如果我们find某个值的话,跟他有多个相同的值,那find的时候查找的是哪一个?
🆗,规定呢它find的是中序遍历遇到的第一个。
它遍历的时候底层走的是中序遍历嘛,所以打印出来才是有序的。
我们可以验证一下的:
我们先
现在有4个1,我们find(1),然后从返回的迭代器位置开始,如果能把4个1都打印出来,就证明是中序的第一个1
在这里插入图片描述
当然我们可以用count统计每个键值的个数:
在这里插入图片描述

不过在实际应用中用multiset一般比较少。

5. map

5.1 认识map

map呢其实就对应搜索二叉树的KV模型,它里面存的是<key, value>结构的键值对。即用来表示具有一 一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代表键值,value表示与key对应的信息。
在这里插入图片描述
在这里插入图片描述
map的接口呢也跟set几乎差不多,有需要注意的我们后面会说。

5.2 pair

那在学习map的使用之前,我们来学一个STL里面的类/结构体模板——pair
在这里插入图片描述

我们来看一下SGI-STL中关于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){}
};

pair的应用:

pair是将2个数据组合成一个数据,当需要这样的需求时就可以使用pair。
(1)STL中的map就是将key和value放在一起来保存(一般first对于key,second对于value)。
(2)另一个应用是,当一个函数需要返回2个数据的时候,可以选择pair

5.3 map的使用

那我们看到map的insert:

在这里插入图片描述
它的第一个版本其实就是接收一个pair类型的对象。
在这里插入图片描述
key是const修饰的,不能修改(即键必须是唯一的),value可以。

那我们来写写代码吧,可以用map写一个我们之前实现的那个英汉互译的词典

map包含在头文件<map>
在这里插入图片描述
当然插入还可以用这个
在这里插入图片描述
那这个make_pair是啥呢?
他其实是库里面提供的一个函数模板
在这里插入图片描述
它可以帮助我们创建一个pair的对象,用它的好处是我们不需要自己去指定类型,因为模板可以自动推导类型。
然后我们来遍历打印一下:
在这里插入图片描述
当然我们也可以这样写:
在这里插入图片描述
那大家看这样可以吗?
在这里插入图片描述
最后一个可以插入成功吗?
在这里插入图片描述
不行,因为键必须是唯一的,不能重复,值可以重复。
一个键只能对应一个值,一个值可以对应多个键
在这里插入图片描述
也可以直接通过find键去查找值
在这里插入图片描述

那我们也用map再写一下统计次数的程序:

跟之前的思路一样,我就直接上代码了
在这里插入图片描述

对map中[]的理解

然后呢,我们发现,map呢还提供了这样一个接口

在这里插入图片描述
它的作用是啥呢?
在这里插入图片描述
首先它接收的参数是键key,如果输入的键与容器中元素的键匹配,就返回该键对应的值的引用。
所以我们刚才的统计次数还可以这样写
在这里插入图片描述
for循环内一行代码,就搞定了

那我们接下来就来研究一下这句代码:

在这里插入图片描述
其实文档上面也解释了,调用这个重载的[]就等效于执行这句代码
(*((this->insert(make_pair(k,mapped_type())).first)).second
所以这个函数大概是这样一个样子:
在这里插入图片描述
我们看到这里面其实去调了一下insert,那我们来仔细研究一下insert的返回值
在这里插入图片描述
我再来解释一下:
首先要知道插入的话呢还是有成功和失败两种情况,因为键值不允许冗余;它返回的是一个pair,第一个模板参数为迭代器,第二个为bool。
当插入成功的时候,pair的first为指向新插入元素的迭代器,second为true,当插入失败的时候(其实就是插入的键已经存在了),那它的first为容器中已存在的那个相同的等效键元素的迭代器,second为false。
所以后面这个bool其实是标识插入成功还是失败。
那我们再来看这个函数
在这里插入图片描述
我们来拆解一下它,要不然不好看
在这里插入图片描述
然后我们再来看里面这个insert
在这里插入图片描述
我们看到这里面insert也是使用make_pair这个函数创建一个pair对象,第一个参数就是我们[]里面放的那个键key,第二个参数传的是啥呢?
我们看到它是调了值value的类型的默认构造(之前我们说过有了模板之后内置类型就也需要有构造函数了),那我们的value是int,默认构造的值是0,所以我们第一次插入刚好它的次数就是0了。然后返回这个次数对应的value的引用,外面对它++,就正好是1。
然后后续插入相同键的话,就插入失败,不会将次数变成0,但是依然返回次数(对应pair中的second)的引用,我们从1继续往上++就行了,
当然它这上面给的有些类型是进行了typedef的,我们不太好看,不过它提供了一个表,大家可以查阅
在这里插入图片描述
所以我们可以将它简化一下,方便看
在这里插入图片描述

所以我们也可以这样玩:

在这里插入图片描述
插入,查找、修改啥的操作这样写
在这里插入图片描述

总结

1. map中的的元素是键值对
2. map中的key是唯一的,并且不能修改
3. 默认按照小于的方式对key进行比较
4. map中的元素如果用迭代器去遍历,可以得到一个有序的序列
5. map的底层为平衡搜索树(红黑树),查找效率比较高 O ( l o g 2 N ) O(log_2 N) O(log2N)
6. 支持[]操作符,operator[]中实际进行插入查找

6. multimap

那和set一样:

除了map还有multimap
在这里插入图片描述

我们看一下

在这里插入图片描述
同样的,跟map相比,multimap允许键key冗余
在这里插入图片描述
所以它的接口里面就没有[]

简单演示一下:

在这里插入图片描述
当然key和value都相同也是可以的。

然后其它的就不多说了,有需要大家可以自己查阅文档。
在这里插入图片描述

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

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

相关文章

使用chatGPT生成提示词,在文心一言生成装修概念图

介绍 家是情感的港湾&#xff0c;而家居装修则是将情感融入空间的艺术。如何在有限的空间里展现个性与美感&#xff0c;成为了现代人关注的焦点。而今&#xff0c;随着人工智能的发展&#xff0c;我们发现了一个新的创意助手——ChatGPT&#xff0c;它不仅为我们带来了更多可能…

linux下绑定进程到指定CPU的操作方法

taskset简介 # taskset Usage: taskset [options] [mask | cpu-list] [pid|cmd [args...]] Show or change the CPU affinity of a process. Options: -a, --all-tasks operate on all the tasks (threads) for a given pid -p, --pid operate on ex…

探索 TypeScript 元组的用例

元组扩展了数组数据类型的功能。使用元组&#xff0c;我们可以轻松构造特殊类型的数组&#xff0c;其中元素相对于索引或位置是固定类型的。由于 TypeScript 的性质&#xff0c;这些元素类型在初始化时是已知的。使用元组&#xff0c;我们可以定义可以存储在数组中每个位置的数…

Android, 笔记+课表的app实现

NoteSchedule: 笔记课表&#xff0c;不同于超表和课程格子等笔记类软件&#xff0c;笔记课表的核心是将课表和笔记进行深度绑定&#xff0c;点击每个课表&#xff0c;就进入到笔记view中&#xff0c;点击其中的item就可以进入到笔记详情&#xff1b; 该应用已上线&#xff0c;…

回归预测 | MATLAB实现POA-CNN-LSTM鹈鹕算法优化卷积长短期记忆神经网络多输入单输出回归预测

回归预测 | MATLAB实现POA-CNN-LSTM鹈鹕算法优化卷积长短期记忆神经网络多输入单输出回归预测 目录 回归预测 | MATLAB实现POA-CNN-LSTM鹈鹕算法优化卷积长短期记忆神经网络多输入单输出回归预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 MATLAB实现POA-CNN…

Creo散热处理结构设计--阵列操作

问题描述&#xff1a; 在某一平面掏孔以散热&#xff0c;如何快速的绘制多孔并掏空处理 解决方式&#xff1a; 采用阵列操作。 1&#xff09;绘制圆、拉伸处理 2&#xff09;选择需要阵列的单元&#xff0c;选择阵列操作&#xff0c;在弹出的界面选择方向&#xff0c;按照…

【Winform学习笔记(七)】Winform无边框窗体拖动功能

Winform无边框窗体拖动功能 前言正文1、设置无边框模式2、无边框窗体拖动方法1、通过Panel控件实现窗体移动2、通过窗体事件实现窗体移动3、调用系统API实现窗体移动4、重写WndProc()实现窗体移动 前言 在本文中主要介绍 如何将窗体设置成无边框模式、以及实现无边框窗体拖动功…

[分享]STM32G070 串口 乱码 解决方法

硬件 NUCLEO-G070RB 工具 cubemx 解决方法 7bit 改为 8bit printf 配置方法 添加头文件 #include <stdio.h> 添加重定向代码 #ifdef __GNUC__#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)#else#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)#endi…

并发多线程篇

线程的基础知识 面试题1&#xff1a;线程与进程的区别&#xff1f; 面试题2&#xff1a;并行和并发有什么区别&#xff1f; 面试题3&#xff1a;创建线程的方式有哪些&#xff1f; 面试题 4&#xff1a;runnable 和 callable 有什么区别&#xff1f; 面试题5&#xff1a;线程…

Git 快速入门

Git 快速入门 文章目录 Git 快速入门一、代码托管平台&#xff08;远程仓库&#xff09;二、安装Git三、Git的命令实践Git 的四个区域Git 管理代码的3个场景Git 工作区的理念Git 工作区的生命周期Git 版本回退Git 文件重命名Git查看版本提交日志Git StashGit分支Git标签 四、创…

Nginx与docker配置安装

目录&#xff1a; Nginx的安装配置&#xff1a; 1、安装依赖包&#xff1a; 2、下载Nginx安装包&#xff1a; 3、解压Nginx压缩包&#xff1a; 4、配置Nginx编译环境&#xff1a; 5、编译并安装Nginx&#xff1a; 6、安装完Nginx后&#xff0c;可以切换到Nginx的安装目录…

【TypeScript】TS接口interface类型(三)

【TypeScript】TS接口interface类型&#xff08;三&#xff09; 【TypeScript】TS接口interface类型&#xff08;三&#xff09;一、接口类型二、实践使用2.1 常规类型2.2 设置属性只读 readonly2.3 设置索引签名2.4 设置可选属性2.5 函数类型接口 一、接口类型 TypeScript中的…

【肌电图信号分析】通道肌电图并查找收缩周期的数量、振幅、最大值和持续时间(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

arcgis宗地或者地块四至权利人信息提取教程

ARCGIS怎样将图斑四邻的名称及方位加入其属性表 以前曾发表过一篇《 如何把相邻图斑的属性添加在某个字段中》的个人心得,有些会员提出了进一步的要求,不但要相邻图斑的名称,还要求有方位,下面讲一下自己的做法。 基本思路是:连接相邻图斑质心,根据连线的角度确定相邻图斑…

SQL-每日一题【1164. 指定日期的产品价格】

题目 产品数据表: Products 写一段 SQL来查找在 2019-08-16 时全部产品的价格&#xff0c;假设所有产品在修改前的价格都是 10 。 以 任意顺序 返回结果表。 查询结果格式如下例所示。 示例 1: 解题思路 1.题目要求我们查找在 2019-08-16 时全部产品的价格&#xff0c;假设所…

处理nacos、tomcat、nginx日志增长过快问题

1.nacos日志清理 修改nacos-logback.xml 将日志级别改为error级&#xff0c;减少info级日志产生量 将<maxHistory>调整为2以下&#xff0c;将 <totalSizeCap>调整为2GB左右 比如&#xff1a; [rootiZ0jlapur4hqjezy8waee0Z logs]# ll -h total 2.1G -rw-r--r-…

设计实现数据库表扩展的7种方式

设计实现数据库表扩展的7种方式 在软件开发过程中&#xff0c;数据库是一项关键技术&#xff0c;用于存储、管理和检索数据。数据库表设计是构建健壮数据库系统的核心环节之一。然而&#xff0c;随着业务需求的不断演变和扩展&#xff0c;数据库表中的字段扩展变得至关重要。 …

[OnWork.Tools]系列 06-屏幕水印

简介 屏幕水印功能主要是在开会分享屏幕的时候在屏幕上增加水印 水印使用 水印启用和颜色设置 水印文字和大小设置 水印间距,透明度,角度调整

centos安装python3的多个版本

标题 原本安装了python3.6&#xff0c;但是又有一个项目需要py3.7&#xff0c;所以需要让两个版本共存 操作 1、下载py3.7 wget https://www.python.org/ftp/python/3.7.3/Python-3.7.3.tgz2、解压 tar -xzvf Python-3.7.3.tgz进入到文件夹 cd Python-3.7.33、安装 本人c…

day10 快速排序 方法重载 和 方法递推

方法重载 斐波拉契数列问题 使用重载思想解决 public static int method(int n){if (n 2 ){return 1 ;}return (n-1)*2method(n-1);}public static int f(int n){if (n 1){return 1;}if (n 2){return 2;}return f(n-1)f(n-2);} 快速排序 思维很简单&#xff0c;类似二…