map和set(c++)

前言

在前面我们在介绍二叉搜索树时我们分别实现了一个key结构和key-val结构,如果我们再进一步完善这棵树,将二叉搜索树升级为红黑树去存储key和key-val那么我们就可以得到我们今天要介绍的主角map和set。当然了标准库的实现还是有很多需要注意的地方,下面我们就带着大家简单的认识一下map和set

序列式容器和关联式容器

前面我们介绍过的的容器如string、vector、list、deque、array、forward_list等,这些容器统称为序列式容器,因为逻辑结构为线性序列的数据结构,两个位置存储的值之间⼀般没有紧密的关联关系

但我们也见过关联式容器,比如priority_queue(优先级队列),它表现为该位置存储的值和其所在的位置强相关,如果随意交换就会破坏这种结构,这就是关联式容器,今天我们介绍的map和set也属于关联式容器

set

手册

我们可以参考手册对set的描述,<set> - C++ Reference (cplusplus.com) 

分类

set可以分为set和multiset,两者本质上的不同在于是否允许键冗余,我们这里讲解set为主,multiset的设计和set是高度一致的

声明

我们先来看看set的声明

template < class T,                        // set::key_type/value_typeclass Compare = less<T>,        // set::key_compare/value_compareclass Alloc = allocator<T>      // set::allocator_type> class set;

第一个参数是存储数据的类型。第二个参数是比较大小的方式可以缺省,默认调用<号比较,基本类型天然支持比较,自定义类型需要实现<重载,也可以自己实现比较的仿函数传入。第三个参数我们还是暂时不关注,这是空间配置器,缺省即可

set底层是⽤红⿊树实现,增删查效率是logN

构造

右值引用的方式我们暂时不关心

前三种构造方式在前面的数据结构种也是非常常见的,不再展开

initializer 列表构造可能比较陌生,我们在这里用一个例子

这种方式初始化还是非常方便的

迭代器

 迭代器遍历是⾛的搜索树的中序,遍历是有序的

首先set的迭代器是双向迭代器,++和--都是支持的

// 正向迭代器
iterator begin();
iterator end();
// 反向迭代器
reverse_iterator rbegin();
reverse_iterator rend();

重载了*操作符,*迭代器可以直接将值取出

 增删查

前面我们介绍过,set是关联式容器,存储的值和其存储的位置具有强关联性,所以不支持对指定元素修改其值,因为这会破坏set的结构

// 单个数据插⼊,如果已经存在则插⼊失败
pair<iterator,bool> insert (const value_type& val);
// 列表插⼊,已经在容器中存在的值不会插⼊
void insert (initializer_list<value_type> il);
// 迭代器区间插⼊,已经在容器中存在的值不会插⼊
template <class InputIterator>
void insert (InputIterator first, InputIterator last);

其中pair<iterator,bool>可以暂时理解为是一种结构体,它的第一个元素是迭代器,第二个元素是布尔值,前者表示指向插入元素位置的迭代器,如果插入失败(因为元素已经存在),它指向容器中与 val 值相等的元素,后者标识是否插入成功

/ 查找val,返回val所在的迭代器,没有找到返回end()
iterator find (const value_type& val);
// 查找val,返回Val的个数
size_type count (const value_type& val) const;

// 删除⼀个迭代器位置的值
iterator erase (const_iterator position);
// 删除val,val不存在返回0,存在返回1
size_type erase (const value_type& val);
// 删除⼀段迭代器区间的值
iterator erase (const_iterator first, const_iterator last);

其中那两个返回迭代器的方法返回值都是一个指向被删除元素后面元素的迭代器

获得区间

有的时候我们希望可以获得值区间的迭代器区间,为此我们介绍两个函数

// 返回⼤于等val位置的迭代器
iterator lower_bound (const value_type& val) const;
// 返回⼤于val位置的迭代器
iterator upper_bound (const value_type& val) const;

如果我们传入的是大于的伪函数,此时这个set如果中序遍历表现为降序lower_boundupper_bound 的行为依然和它们在升序情况下类似,但它们的“查找方向”会变化

表现为大于等转为小于等,大于转为小于

再谈multiset和set

multiset和set的使⽤基本完全类似,主要区别点在于multiset⽀持值冗余,那insert/find/count/erase都围绕着⽀持值冗余有所差异

相⽐set不同的是,multiset是排序,但是不去重

相⽐set不同的是,x可能会存在多个,find查找中序的第⼀个

相⽐set不同的是,count会返回x的实际个数 0和1=》0和几

相⽐set不同的是,erase给值时会删除所有的x

map

手册

我们可以参考手册对map的描述,<map> - C++ Reference (cplusplus.com)

分类

map还是可以按照是否允许键冗余分为map和multimap,两者本质上的不同在于是否允许键冗余,我们这里讲解map为主,multimap的设计和map是高度一致的

声明

我们还是先来看看声明

template < class Key, // map::key_type
class T, // map::mapped_type
class Compare = less<Key>, // map::key_compare
class Alloc = allocator<pair<const Key,T> > //map::allocator_type
> class map;

Key类型是键的类型,T是值的类型,Compare是比较的伪函数,Alloc是空间配置器,我们暂时不关注

遍历默认按key的升序顺序,因为底层是⼆叉搜索树,迭代器遍历⾛的中序

map⽀持修改value数据,不⽀持修改key数据,修改关键字数据,破坏了底层搜索树的结构

pair类型

前面我们在介绍set的时候我们见过pair类型,但我们只是简单的介绍了一下,现在我们详细的讲解一下

首先我们需要知道,map底层的红⿊树节点中的数据,使⽤pair<Key,T>存储键值对数据

pair - C++ Reference (cplusplus.com),先来看看手册

template <class T1, class T2> struct pair;

它也是一个类模板,它会存储两个类型的值让他们称为一个整体,这是它本来的作用,我们可以简单的看下实现

template <class T1, class T2>
struct pair //struct可以修饰类,其内部所有的成员默认公开
{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的方法
template <class T1,class T2>
inline pair<T1,T2> make_pair (T1 x, T2 y)
{return ( pair<T1,T2>(x,y) );
}

构造

迭代器

双向迭代器 

// 正向迭代器
iterator begin();
iterator end();
// 反向迭代器
reverse_iterator rbegin();
reverse_iterator rend();

增删改查

这里比set多支持改是因为我们支持该值,键依然不被允许修改

value_type -> pair<const key_type,mapped_type>
// 单个数据插⼊,如果已经key存在则插⼊失败,key存在相等value不相等也会插⼊失败
pair<iterator,bool> insert (const value_type& val);
// 列表插⼊,已经在容器中存在的值不会插⼊
void insert (initializer_list<value_type> il);
// 迭代器区间插⼊,已经在容器中存在的值不会插⼊
template <class InputIterator>
void insert (InputIterator first, InputIterator last);

key_type -> The first template parameter (Key)// 查找k,返回k所在的迭代器,没有找到返回end()
iterator find (const key_type& k);
// 查找k,返回k的个数
size_type count (const key_type& k) const;

// 删除⼀个迭代器位置的值
iterator erase (const_iterator position);
// 删除k,k存在返回0,存在返回1
size_type erase (const key_type& k);
// 删除⼀段迭代器区间的值
iterator erase (const_iterator first, const_iterator last);

我们可以通过iterator修改,也可以使用operator[]修改

operator[]不仅仅⽀持修改,还⽀持插⼊数据和查找数据

insert插⼊⼀个pair<key, T>对象时,会返回一个pair<迭代器, 布尔值>的pair

迭代器会指向键为key的位置,布尔值表示是否插入成功

⽆论插⼊成功还是失败,返回pair<iterator,bool>对象的first都会指向key所在的迭代器

insert插⼊失败时充当了查找的功能,正是因为这⼀点,insert可以⽤来实现operator[]

看一下实现

mapped_type& operator[] (const key_type& k)
{
pair<iterator, bool> ret = insert({ k, mapped_type() });
iterator it = ret.first;
return it->second;
}

获得区间

// 返回⼤于等k位置的迭代器
iterator lower_bound (const key_type& k);
// 返回⼤于k位置的迭代器
const_iterator lower_bound (const key_type& k) const;

如果我们传入的是大于的伪函数,此时这个map如果中序遍历表现为降序lower_boundupper_bound 的行为依然和它们在升序情况下类似,但它们的“查找方向”会变化

表现为大于等转为小于等,大于转为小于

operator->

map的迭代基本都使⽤operator->,这⾥省略了⼀个->

// 第⼀个->是迭代器运算符重载,返回pair*,第⼆个箭头是结构指针解引⽤取
pair数据
//cout << it.operator->()->first << ":" << it.operator->()->second << endl;
cout << it->first << ":" << it->second << endl;

再谈multimap和map

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

结语

以上便是今天的全部内容。如果有帮助到你,请给我一个免费的赞。

因为这对我很重要。

编程世界的小比特,希望与大家一起无限进步。

感谢阅读!

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

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

相关文章

玩机搞机基本常识-----如何在 Android 中实现默认开启某个功能 修改方法列举

我们有时候需要对安卓系统进行修改。实现其中的某些功能。让用户使用得心应手。节约时间。那么如果要实现系统中的有些功能选项开启或者关闭。就需要对系统有一定的了解。那么在 Android 中实现默认开启某个功能可以通过以下几种方式&#xff1a; 一、在应用的设置中添加选项 …

C语言练习

题目: 1.如果在int型变量的声明中为变量赋一个实数值(如3.12或4.6)的初始值会怎样呢&#xff1f;请打一段代码来看看 分析&#xff1a;……不用分析&#xff0c;开个玩笑&#xff0c;虽然很简单但是还是按照惯例水上一波数字 1.首先按照题目要求用函数类型int整型给变量赋值…

鸿蒙网络管理模块05——数据流量统计

如果你也对鸿蒙开发感兴趣&#xff0c;加入“Harmony自习室”吧&#xff01;扫描下方名片&#xff0c;关注公众号&#xff0c;公众号更新更快&#xff0c;同时也有更多学习资料和技术讨论群。 1、概述 HarmonyOS供了基于物理网络的数据流量统计能力&#xff0c;支持基于网卡/U…

【PS2020】Adobe Photoshop 2020 中文免费版

photoshop 2020是全球最大的图像处理软件&#xff0c;为用户提供了广泛的专业级润饰工具套件&#xff0c;集成了专为激发灵感而设计的强大编辑功能&#xff0c;帮助用户制作出满意的图片效果&#xff0c;是很多摄影师、广告师等专业人员必备的一款图像及照片后期处理大型专业软…

网络受限情况下安装openpyxl模块提示缺少Jdcal,et_xmlfile

1.工作需要处理关于Excel文件内容的东西 2.用公司提供的openpyxl模块总是提示缺少jdcal文件,因为网络管控,又没办法直接使用命令下载&#xff0c;所以网上找了资源&#xff0c;下载好后上传到个人资源里了 资源路径 openpyxl jdcal et_xmlfile 以上模块来源于&#xff1a;Py…

Java-进阶二

单列集合&#xff1a; ----------List ArrayList的源代码分析&#xff08;扩容原理&#xff09; 1 使用空参构造的集合&#xff0c;在底层创建一个容量为0的数组。2 添加第一个元素时&#xff0c;底层会扩容创建一个容量为10的数组。3 存满时会扩容1.5倍。4 如果一次添加多个…

大模型基础:基本概念、Prompt、RAG、Agent及多模态

随着大模型的迅猛发展&#xff0c;LLM 作为人工智能的核心力量&#xff0c;正以前所未有的方式重塑着我们的生活、学习和工作。无论是智能语音助手、自动驾驶汽车&#xff0c;还是智能决策系统&#xff0c;大模型都是幕后英雄&#xff0c;让这些看似不可思议的事情变为可能。本…

Redis SpringBoot项目学习

Redis 是一个高性能的key-value内存数据库。它支持常用的5种数据结构&#xff1a;String字符串、Hash哈希表、List列表、Set集合、Zset有序集合 等数据类型。 Redis它解决了2个问题&#xff1a; 第一个是&#xff1a;性能 通常数据库的读操作&#xff0c;一般都要几十毫秒&…

虚拟机没有网络怎么解决

CentOS7为例 进入虚拟网络编辑器 1.更改设置 2.选中NAT模式点击3点击移除网络 4添加网络&#xff0c;随便选一个 5.点开NAT设置&#xff0c;记住网关 6.DHCP设置&#xff0c;注意虚拟机设置ip必须在起始ip和结束ip范围内 进入虚拟机网络适配器&#xff0c;自定义选中第4步操作…

【Kubernetes】常见面试题汇总(五十二)

目录 116. K8S 集群服务暴露失败&#xff1f; 117.外网无法访问 K8S 集群提供的服务&#xff1f; 特别说明&#xff1a; 题目 1-68 属于【Kubernetes】的常规概念题&#xff0c;即 “ 汇总&#xff08;一&#xff09;~&#xff08;二十二&#xff09;” 。 题目 69-…

torchvision.transforms.Resize()的用法

今天我在使用torchvision.transforms.Resize()的时候发现&#xff0c;一般Resize中放的是size或者是(size,size)这样的二元数。 这两个里面&#xff0c;torchvision.transforms.Resize((size,size))&#xff0c;大家都很清楚&#xff0c;会将图像的h和w大小都变成size。 但是…

大学生课程设计报告--基于JavaGUI的贪吃蛇

前言 ​ 贪吃蛇游戏是一个基础且经典的视频游戏,它适合作为学习编程的人进行一些更深入的学习,可以更加了解关于循环,函数的使用,以及面向对象是如何应用到实际项目中的; ​ 不仅如此,贪吃蛇游戏的规则在思考后可以拆分,有利于学生将更多精力去设计游戏的核心逻辑,而…

TM1618控制共阳极数码管的数据传送问题

数据传送中的问题 首先每个字节是按照一个地址写入的&#xff0c;而共阳极数码管的公共端是SEG引脚连接的。这使得数码管显示的编码是按照竖向的字节。如下图所示中&#xff0c;横向是公共端&#xff0c;竖向是实际编码字符字节。 数据转换方式 这样可以一次写入所有需要显示…

腾讯云SDK项目管理

音视频终端 SDK&#xff08;腾讯云视立方&#xff09;控制台提供项目管理功能&#xff0c;您可参照以下步骤为您的应用快速添加音视频通话能力和多人音视频互动能力。 若需正式开发并上线音视频应用&#xff0c;请在完成创建后&#xff0c;参照 集成指南 进行开发包下载、集成…

yolov11人物背景扣除

有时候我们需要对图片进行背景扣除和替换,本文将基于yolov11对一张图片进行背景扣除,对视频的处理同理。 安装 pip install ultralytics 2 、获取测试图片 3、代码 from ultralytics import YOLO import cv2 import nu

【概率论】泊松分布

泊松分布 若 &#xff0c;则 归一性 例子 泊松分布多出现在当X表示一定时间或一定空间内出现的事件的个数这种场合&#xff0c;如在一定时间内某交通路口所发生的事故的个数。 将泊松分布假设为二项分布 假设条件: &#xff08;1&#xff09;泊松分布一般为一段时间或一…

ChatGPT:引领人工智能新潮流!

一、ChatGPT 是什么&#xff1f; 1. ChatGPT 的强大功能和广泛应用。 ChatGPT 作为一款先进的 AI 语言模型&#xff0c;拥有众多强大功能。它可以进行文本生成、文本分类、情感分析、机器翻译等多种自然语言处理任务。同时&#xff0c;ChatGPT 还能进行对话式交互&#xff0c;…

C++版iwanna2

第二篇目录 程序的流程图程序游玩的效果下一篇博客要说的东西 程序的流程图 #mermaid-svg-lFW0ZjCdi5Xvl3gE {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-lFW0ZjCdi5Xvl3gE .error-icon{fill:#552222;}#mermaid-s…

信息安全工程师(40)防火墙技术应用

一、防火墙的基本概念 防火墙是一种网络安全设备&#xff0c;用于监控和控制网络流量&#xff0c;以保护网络免受未经授权的访问和攻击。它可以是装配多张网卡的通用计算机&#xff0c;也可能是通用的物理设备。防火墙通过在网络之间设置访问控制策略&#xff0c;对进出的通信流…

Window系统编程 - 文件操作

前言 各位师傅大家好&#xff0c;我是qmx_07&#xff0c;今天主要介绍使用windows系统编程操作读写文件 文件 CreateFile()函数讲解 介绍:该函数用于打开文件或者I/O流设备&#xff0c;文件、文件流、目录、物理磁盘、卷、控制台缓冲区、磁带驱动器、通信资源、mailslot 和…