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.xdnf.cn/news/1559143.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

植物大战僵尸修改器-MFC

创建项目 创建mfc应用 基于对话框 打开资源视图下的 IDD_MFCAPPLICTION2_DIALOG 限制对话框大小 将属性中Border的值改为对话框外框 删除对话框中原有的控件 属性-外观-Caption 设置对话框标题 工具箱中拖放一个按钮 修改按钮名称 将按钮ID改为IDC_COURSE 在MFCApplication2…

k8s微服务

一 、什么是微服务 用控制器来完成集群的工作负载&#xff0c;那么应用如何暴漏出去&#xff1f;需要通过微服务暴漏出去后才能被访问 Service是一组提供相同服务的Pod对外开放的接口。 借助Service&#xff0c;应用可以实现服务发现和负载均衡。 service默认只支持4层负载均…

QT安装成功后-在创建项目时,发现仅有项目名文件

&#xff08;1&#xff09;QT安装成功后&#xff0c;发现仅有项目名文件其他可编辑文件缺失 &#xff08;2&#xff09;点击文件名左上角的感叹号显示【No kits are enabled for this project. Enable】 小编在尝试多次后发现&#xff0c;可以通过以下方式解决&#xff1a;QT软…

接着上一篇stp 实验继续

理论看上一篇&#xff0c;我们直接实验 首先找出&#xff52;&#xff4f;&#xff4f;&#xff54; 桥 很明显 &#xff53;&#xff57;&#xff11; 为&#xff52;&#xff4f;&#xff4f;&#xff54; 桥&#xff0c;所谓&#xff53;&#xff57;&#xff11;  &a…

从Hinton获得今年的诺贝尔物理学奖说起

“深度人工智能”是成都深度智谷科技旗下的人工智能教育机构订阅号&#xff0c;主要分享人工智能的基础知识、技术发展、学习经验等。此外&#xff0c;订阅号还为大家提供了人工智能的培训学习服务和人工智能证书的报考服务&#xff0c;欢迎大家前来咨询&#xff0c;实现自己的…

JavaSE——集合1:Collection接口(Iterator和增强for遍历集合)

目录 一、集合框架体系(重要) 二、集合引入 (一)集合的理解与好处 三、Collection接口 (一)Collection接口实现类的特点 (二)Collection接口常用方法 (三)Collection接口遍历元素的方式(Iterator和增强for) 1.使用Iterator(迭代器) 1.1Iterator(迭代器)介绍 1.2Itera…

OmniH2O——通用灵巧且可全身远程操作并学习的人形机器人(其前身H2O是HumanPlus的重要参考)

前言 由于我司一直在针对各个工厂、公司、客户特定的业务场景&#xff0c;做解决方案或定制开发&#xff0c;所以针对每一个场景&#xff0c;我们都会反复考虑用什么样的机器人做定制开发 于此&#xff0c;便不可避免的追踪国内外最前沿的机器人技术进展&#xff0c;本来准备…

指针函数C++

指针函数概念 指针函数在C中是一种特殊类型的函数。从本质上讲&#xff0c;它是一个函数&#xff0c;不过其返回值是一个指针类型的数据。例如&#xff0c;像int* plusfunction(int a, int b);这样的函数声明&#xff0c;plusfunction就是一个指针函数&#xff0c;它接受两个i…

【学习笔记】Linux系统基础知识4 —— date命令详解

提示&#xff1a;学习Linux系统基础命令 date 命令详解 一、前期准备 1.已经正确安装并成功进入Linux系统 说明&#xff1a;本实验采用的 Redhat 系统&#xff08;因系统不一致&#xff0c;可能部分显示存在差异&#xff09; 二、学习内容 1、date命令 1. 功能说明 date …

SpringBoot实现电子文件签字+合同系统

​ 博客主页: 南来_北往 系列专栏&#xff1a;Spring Boot实战 在现代企业中&#xff0c;合同管理和电子文件签字已成为日常运营不可或缺的一部分。为了提升效率和安全性&#xff0c;我们可以使用SpringBoot框架来实现一个电子文件签字和合同管理系统。本文将详细介绍如何…

ITMS-90899: Macs with Apple silicon support issue

文章目录 1.问题2.解决方法2.1 直接忽略这个警告邮件&#xff0c;不会影响app的正常上传2.2 通过在项目的Info.plist文件中加LSMinimumSystemVersion : 12.0 来消除警告 参考链接&#xff1a; 1.问题 ITMS-90899: Macs with Apple silicon support issue - The app isn‘t comp…

机器学习入门(一)

一、机器学习概述 1、人工智能 像人一样智能的综合与分析&#xff0c;机器模拟人类。 是一个系统&#xff0c;像人那样思考&#xff0c;像人那样理性思考。 是一个系统&#xff0c;像人那样活动&#xff0c;像人那样合理的系统 2、机器学习 让机器自动学习&#xff0c;而不…

公司防泄密软件哪个好?6款公司内部文件防泄密软件,2024超好用推荐!

企业的核心机密就如同生命之源&#xff0c;然而&#xff0c;数据泄露的风险也随之而来&#xff0c;让不少企业头疼不已。 面对这一挑战&#xff0c;选择一款高效、可靠的防泄密软件显得尤为重要。 那么&#xff0c;公司防泄密软件哪个好&#xff1f; 接下来&#xff0c;就让我…

posix接口与system V接口及其异同

POSIX接口和System V接口是用于多线程和进程间通信的两种主要编程接口。它们各自有不同的特点、功能和适用场景。以下是对这两种接口的详细介绍及其异同点。 POSIX接口 特点 标准化: POSIX&#xff08;可移植操作系统接口&#xff09;是由IEEE制定的标准&#xff0c;旨在提供统…

​ ​视觉任务大一统!图像生成,编辑,翻译三合一!全能视觉助手PixWizard来袭!

文章链接&#xff1a;https://arxiv.org/pdf/2409.15278 github链接&#xff1a;https://github.com/AFeng-x/PixWizard 亮点直击 任务统一&#xff1a;针对视觉任务的多样性&#xff0c;提出将其框架化为图像到图像的转换问题&#xff0c;并通过后处理将生成的可视化效果转化…

瑞华技术募资额巨降过半:业绩大幅下滑,信用期外应收账款占比高

《港湾商业观察》黄懿 上市的节奏有快有慢&#xff0c;常州瑞华化工工程技术股份有限公司&#xff08;下称“瑞华技术”&#xff0c;920099.BJ&#xff09;自2023年3月被北交所受理后&#xff0c;于2024年8月29日获得注册批文&#xff0c;9月25日正式挂牌上市。 据了解&#…

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

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

前端性能优化全面指南

前端性能优化是提升用户体验的关键&#xff0c;页面加载速度、响应时间和交互流畅度直接影响用户的留存率和满意度。以下是常用的前端性能优化方法&#xff0c;从网络层、资源加载、JavaScript 执行、渲染性能等方面进行全方位优化。 减少 HTTP 请求 合并文件&#xff1a;将多…

文献下载/影响因子查询/文献检索/文献翻译平台推荐

文献下载平台 科研通 文献互助平台 - 科研通(AbleSci.com) 每天签到可领10积分&#xff0c;右上角求助文献&#xff0c;一篇10积分&#xff0c;基本实现免费下载。 尽量输入doi&#xff08;类似文献id&#xff09;&#xff0c;如果没有doi则输入标题作者摘要等信息&#xff0…

YOLO11模型推理 | 目标检测与跟踪 | 实例分割 | 关键点估计 | OBB旋转目标检测

前言 本文分享YOLO11的模型推理&#xff0c;检测任务包括物体分类、目标检测与跟踪、实例分割 、关键点估计、旋转目标检测等。 首先安装YOLO11 官方默认安装方式 通过运行 pip install ultralytics 来快速安装 Ultralytics 包 安装要求&#xff1a; Python 版本要求&…