【C++容器map】map的相关用法

图片名称

🎉博主首页: 有趣的中国人

🎉专栏首页: C++进阶

🎉其它专栏: C++初阶 | 初阶数据结构 | Linux


在这里插入图片描述


本篇文章主要讲解 C++容器之map相关用法 的相关内容

文章目录

  • `1. map的介绍`
  • `2. map的使用`
    • ==<font size=5 color = #0000ff>💯map的模板参数==
    • ==<font size=5 color = #0000ff>💯map的插入==
      • ==<font size=4 color = #0000ff>💥✨关于pair的构造==
      • ==<font size=4 color = #0000ff>💥✨map显示定义pair对象传入插入==
      • ==<font size=4 color = #0000ff>💥✨map传入pair匿名对象插入==
      • ==<font size=4 color = #0000ff>💥✨map的makepair插入==
      • ==<font size=4 color = #0000ff>💥✨map的initializer_list插入==
    • ==<font size=5 color = #0000ff>💯map的迭代器==
    • ==<font size=5 color = #0000ff>💯关于map的 [ ] 操作==
      • ==<font size=4 color = #0000ff>💥✨[ ] 的使用说明==
  • `3.相关OJ题目`

1. map的介绍

C++ Reference 介绍链接:

map文档介绍

总结

  1. map是关联容器,它按照特定的次序(按照key来比较)存储由键值key和值value组合而成的元素。(KV模型,之前文章讲过)
  2. map中,键值key通常用于排序和惟一地标识元素,而值value中存储与此键值key关联的内容。键值key和值value的类型可能不同,并且在map的内部,keyvalue通过成员类型value_type绑定在一起,为其取别名称为pair:
  3. 在内部,map中的元素总是按照键值key进行比较排序的。
  4. map中通过键值访问单个元素的速度通常比unordered_map(哈希表)容器慢,但map允许根据顺序对元素进行直接迭代(即对map中的元素进行迭代时,可以得到一个有序的序列)。
  5. map支持下标访问符,即在[]中放入key,就可以找到与key对应的value
  6. map通常被实现为二叉搜索树(更准确的说:平衡二叉搜索树(红黑树))。

2. map的使用

💯map的模板参数

在这里插入图片描述

  • key: 键值对中key的类型;
  • T: 键值对中value的类型;
  • Compare: 比较器的类型,map中的元素是按照key来比较的,缺省情况下按照小于来比较,一般情况下(内置类型元素)该参数不需要传递,如果无法比较时(自定义类型),需要用户自己显式传递比较规则(一般情况下按照函数指针或者仿函数来传递);
  • Alloc:通过空间配置器来申请底层空间,不需要用户传递,除非用户不想使用标准库提供的空间配置器。

💯map的插入

在这里插入图片描述

上面讲过map是一个KV模型,即第一个存的是关键字Key,第二个存的是值ValueC++为了方便,把这两个要存的值放在了一个结构体pair中,并将他typedef成了value_type,一般称pair为键值对。


我们可以看一下pair类中的成员变量和成员函数

在这里插入图片描述


其实也很容易能看懂对吧,就和我们上面介绍的一样,需要注意的是有两个构造函数,一个是默认的(无参),一个是有const类型参数的。


💥✨关于pair的构造


咱们先学习一下pair的构造才能继续学习map的插入操作:

在这里插入图片描述

这里可以看到pair是支持:

  1. 默认的无参构造;
  2. 拷贝构造;
  3. initializer_list的构造(注意这里后面会用)。

除此之外还支持一个make_pair的构造:

在这里插入图片描述

看的出来make_pair是一个函数模板,它会根据我们传入的数据来推断出数据类型,返回类型也是一个pair

pair构造的总结

pair最重要的还是两个构造方式:

  1. make_pair 的构造;
  2. initializer_list 的构造。

💥✨map显示定义pair对象传入插入


这个很好理解,直接看代码吧:
void test_map1()
{map<string, string> dict;pair<string, string> kv1("sort", "排序");dict.insert(kv1);
}

💥✨map传入pair匿名对象插入


这个也很好理解吧,直接看代码吧:
dict.insert(pair<string, string>("tree", "树"));

💥✨map的makepair插入


这边就是用到库中的make_pair的函数,他会自动帮我们生成pair类型的对象,还会帮我们推出类型,看代码

dict.insert(make_pair("heap", "堆"));

💥✨map的initializer_list插入


我们刚才说过pair支持initializer_list的构造方式,所以我们可以用这个性质让他隐式类型转换成pair然后再插入(多参数类型的构造函数也支持隐式类型转换,只是要传入initializer_list类型)。

dict.insert({ "strawberry","草莓" });

💯map的迭代器

map也同样支持正向和反向迭代器,用法也类似,就拿我们刚才写的插入的代码来看,以下是迭代器的用法。

迭代器用法代码示例:

void test_map1()
{map<string, string> dict;pair<string, string> kv1("apple","苹果");dict.insert(kv1);dict.insert(pair<string, string>("banana", "香蕉"));dict.insert(make_pair("pineapple", "菠萝"));dict.insert({ "blueberry","蓝莓" });map<string, string>::iterator it = dict.begin();while (it != dict.end()){cout << (*it).first << " " << (*it).second << endl;++it;}
}

很多人可能有疑问为什么这里的访问不是*it呢?我们可以回忆以下关于list的迭代器的实现方法,list的迭代器类中只有一个Node*类型的指针,为了解引用满足需求,我们进行了运算符重载,解引用返回值的是node->val


所以这里也类似,返回值是pair类型,因此,根据我们刚才对pair的了解,pair->firstKeypair->secondValue,这样理解起来就很简单了对吧。

除此之外,我们在模拟实现list的时候,是不是也运算符重载了一个->,同样的map这里也有这个操作,返回值也可以和list的类比以下,list返回的是val的地址,这里返回值类型是pair*

那么就可以有以下的用法:

while (it != dict.end())
{cout << (*it).first << " " << (*it).second << endl;// 不省略的写法cout << it.operator->()->first << " " << it.operator->()->second << endl;// 这里之前讲过,两个->省略成一个->cout << it->first << " " << it->second << endl;++it;
}

对于map来说,Key不可以修改,而Val可以修改,例如上面的代码稍加修改:

while (it != dict.end())
{it->second += 'x';// 下面会报错//it->first += 'x';cout << (*it).first << " " << (*it).second << endl;cout << it.operator->()->first << " " << it.operator->()->second << endl;cout << it->first << " " << it->second << endl;++it;
}

在这里插入图片描述

💯关于map的 [ ] 操作


我们在之前讲过统计水果出现的次数的代码,可以用KV模型来实现,我们尝试用map模拟一下:

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

其实这里可以用 [] 操作直接搞定:

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

💥✨[ ] 的使用说明

在这里插入图片描述

解释

  1. 注意到 [ ] 的返回值是mapped_type,即Value,传入类型是key_type,即Key
  2. 如果在容器中找到了匹配的key,就会返回value的引用;
    如果没找到所能够匹配的key,他就会插入这个新元素,并返回它的value的引用。
  3. 与一下代码行为相同:

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

我们仔细分析一下他给出的行为相同的代码,先看一下insert的返回值:

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

解释

  1. 单元素的插入返回类型是pair,并且pair的第一个元素类型是iterator,第二个元素类型是bool
  2. 对于迭代器:如果插入成功(key不存在),就会返回此元素所在位置的迭代器,如果插入不成功(元素已经存在),就会返回相同元素所在位置的迭代器;
  3. 对于bool:如果插入成功(key不存在),就是true,如果插入不成功(元素已经存在),就是false
  4. 所以不管插入成功与否,都会返回key节点所在的迭代器。

类似于这段代码

V& operator[] (const K& key)
{pair<iterator, bool> ret = insert(make_pair(key, V()));iterator it = ret.first;return it->second;
}

所以[]就有查找和插入两种操作:

void test_map3()
{map<string, string> dict;// 插入dict["insert"];// 插入 + 修改dict["left"] = "左边";// 查找cout << dict["left"] << endl;// 修改dict["insert"] = "插入";
}

3.相关OJ题目

题目链接: 随机链表的复制
题目思路:这题的难点主要在random指针的深拷贝:

  1. 我们可以在每次新建一个结点之后用map来存储原始节点和新建节点的对应关系;
  2. 然后再遍历原始链表,针对每个节点,我们就可以查看其random指针指向的位置,然后利用map,就可以找到这个目标节点在复制链表中对应的新节点。
  3. 主要就是这行代码:copy->random = RandomMap[cur->random](我只能说这个思路太牛逼了)。

实现代码

class Solution 
{
public:Node* copyRandomList(Node* head) {Node* cur = head;Node* newhead = nullptr, *tail = nullptr;map<Node*, Node*> randomMap;while (cur){if (newhead == nullptr){newhead = tail = new Node(cur->val);}else{tail->next = new Node(cur->val);tail = tail->next; }// 每次新增cur和tail的映射关系randomMap[cur] = tail;cur = cur->next;}cur = head;Node* copy = newhead;while (cur){// 判断为空的情况if (cur->random == nullptr){copy->random = nullptr;}else{// 这个思路的点睛之笔,以cur->random作为key,找到val,// 这个val就是复制链表中copy->random所指的位置copy->random = randomMap[cur->random];}cur = cur->next;copy = copy->next;}return newhead;}
};

就很完美地过啦,而且效率很高:


在这里插入图片描述

题目链接: 前K个高频单词
题目思路:

  1. 这题首先要去重,用map就可以;
  2. 然后要按照出现的次数排序,可以先把它放到vector中,然后按照降序排序(仿函数);
  3. 最后再把它放在返回的vector中即可。

但是这题的难点是要按照字典序排序
解决方法 1.

  • 首先我们去重的时候用map肯定是按照字典序排序的,但是把它放到vector中之后,然后按照出现的次数进行排序,因为sort的底层是快排(不稳定),所以当次数相同时字典序大的肯定再前面了,因此要用稳定的排序,库中有stable_sort(底层用的是归并排序),可以解决这个问题。

解决方法 2.

  • 除此之外,还有一种方法,在仿函数中添加出现次数相同的情况下按照字典序排序即可。

解决方法1代码

struct kvComp
{bool operator()(const pair<string, int>& kv1, const pair<string, int>& kv2){return kv1.second > kv2.second;}
};class Solution 
{
public:vector<string> topKFrequent(vector<string>& words, int k) {map<string, int> countMap;for (auto& e : words){countMap[e]++;}vector<pair<string,int>> v(countMap.begin(),countMap.end());stable_sort(v.begin(),v.end(),kvComp());vector<string> ret;for (size_t i = 0; i < k; ++i){ret.push_back(v[i].first);}return ret;}
};

解决方法2代码

struct kvComp
{bool operator()(const pair<string, int>& kv1, const pair<string, int>& kv2){return kv1.second > kv2.second || (kv1.second == kv2.second && kv1.first < kv2.first);}
};class Solution 
{
public:vector<string> topKFrequent(vector<string>& words, int k) {map<string, int> countMap;for (auto& e : words){countMap[e]++;}vector<pair<string,int>> v(countMap.begin(),countMap.end());sort(v.begin(),v.end(),kvComp());vector<string> ret;for (size_t i = 0; i < k; ++i){ret.push_back(v[i].first);}return ret;}
};

在这里插入图片描述

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

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

相关文章

在做题中学习(48):朴素的二分查找

. - 力扣&#xff08;LeetCode&#xff09; 解法一&#xff1a; 暴力求解 for循环中&#xff0c;从nums[0]枚举到nums[n-1]&#xff0c;依次判断&#xff0c;返回 target的值。 时间复杂度 : O(N) :因为要遍历一遍数组 解法二&#xff1a;二分查找 因为此数组为有序的…

Springboot+Vue+小程序+基于微信小程序护农远程看护系统

开发平台为idea&#xff0c;maven管理工具&#xff0c;Mybatis操作数据库&#xff0c;根据市场数字化需要为农户打造小程序可远程查看农场的种植情况。项目是调试&#xff0c;讲解服务均可有偿获取&#xff0c;需要可在最下方QQ二维码处联系我。 SpringbootVue小程序&#xff…

QT——简易计算机(从0开始)

目录 一、题目描述&#xff1a; 二、创建工程&#xff1a; 三、UI界面设计&#xff1a; 四、程序编写&#xff1a; 五、总程序&#xff1a; 六、windows可执行文件 七、实现效果 一、题目描述&#xff1a; 创建一个简单的图形用户界面(GUI),包括一个文本框用于显示计算结…

Cisco IOS XE Web UI 权限提升漏洞复现(CVE-2023-20198)

0x01 产品简介 Web UI 是一种基于GUI的嵌入式系统管理工具,能够提供系统配置、简化系统部署和可管理性以及增强用户体验。它带有默认映像,因此无需在系统上启用任何内容或安装任何许可证。Web UI 可用于构建配置以及监控系统和排除系统故障,而无需CLI专业知识。 0x02 漏洞…

Linux命令大全 以及搭建hadoop

Liunx系统目录 ├── bin -> usr/bin # 用于存放二进制命令 ├── boot # 内核及引导系统程序所在的目录 ├── dev # 所有设备文件的目录&#xff08;如磁盘、光驱等&#xff09; ├── etc # 配置文件默认路径、服务启动命令存放目录 ├── home # 用户家目录&#…

快速了解Django:核心概念解析与实践指南

title: 快速了解Django&#xff1a;核心概念解析与实践指南 date: 2024/5/1 20:31:41 updated: 2024/5/1 20:31:41 categories: 后端开发 tags: Django核心路由系统视图系统ORM管理中间件Web框架登录装饰器 第一章&#xff1a;Django简介 背景和发展历程&#xff1a; Djan…

用Jenkins实现cherry-pick多个未入库的gerrit编译Android固件

背景: 在做Android固件开发的时候,通常我们可以利用gerrit-trigger插件,开发者提交一笔的时候自动触发jenkins编译,如果提交的这一笔的编译依赖其他gerrit才能编译过,我们可以在commit message中加入特殊字段,让jenkins在编译此笔patch的时候同时抓取依赖的gerrit代码下…

JSON教程(非常详细)

参考文章来源&#xff1a;JSON教程&#xff08;非常详细&#xff09; 目录 JSON JSON 发展史 为什么要使用 JSON&#xff1f; JSON 的不足 存储格式 使用场景 1) 定义接口 2) 序列化 3) 生成 Token 4) 配置文件 JSON语法规则 JSON 与 JavaScript 对象的区别 JSON数…

第7篇:创建Nios II工程之控制LED<二>

Q&#xff1a;上一期我们完成了Quartus硬件工程部分&#xff0c;本期我们创建Nios II软件工程这部分。 A&#xff1a;创建完BSP和Nios II Application之后&#xff0c;在source文件main.c中添加LED控制代码&#xff1a;system.h头文件包含了Platform Designer系统中IP的硬件信…

Kubernetes - CentOS7搭建k8s_v1.18集群高可用(kubeadm/二进制包部署方式)实测配置验证手册

Kubernetes - CentOS7搭建k8s集群高可用&#xff08;kubeadm/二进制包部署方式&#xff09;实测配置验证手册 前言概述&#xff1a; 一、Kubernetes—k8s是什么 Kubernetes 这个名字源于希腊语&#xff0c;意为“舵手“或”飞行员"。 Kubernetes&#xff0c;简称K8s&#…

Qwen-Audio:推动通用音频理解的统一大规模音频-语言模型(开源)

随着人工智能技术的不断进步&#xff0c;音频语言模型&#xff08;Audio-Language Models&#xff09;在人机交互领域变得越来越重要。然而&#xff0c;由于缺乏能够处理多样化音频类型和任务的预训练模型&#xff0c;该领域的进展受到了限制。为了克服这一挑战&#xff0c;研究…

51单片机入门(一)

1. 51单片机的基础介绍 2. RAM和ROM的区别 总体而言&#xff0c;RAM和ROM在计算机系统中起着不同的角色&#xff0c;RAM用于临时存储运行时数据&#xff0c;而ROM用于存储永久性的固件和系统程序。 3. 为什么叫51单片机 因为51系列单片机都是使用Intel 8031指令系统的单片机…

太速科技-多路PCIe的阵列计算全国产化服务器

多路PCIe的阵列计算全国产化服务器 多路PCIe的阵列计算全国产化服务器以国产化处理器&#xff08;海光、飞腾ARM、算能RSIC V&#xff09;为主板&#xff0c;扩展6-8路PCIe3.0X4计算卡&#xff1b; 计算卡为全国产化的AI处理卡&#xff08;瑞星微ARM&#xff0c;算能AI&#x…

《QT实用小工具·五十》动态增删数据与平滑缩放移动的折线图

1、概述 源码放在文章末尾 该项目实现了带动画、带交互的折线图&#xff0c;包含如下特点&#xff1a; 动态增删数值 自适应显示坐标轴数值 鼠标悬浮显示十字对准线 鼠标靠近点自动贴附 支持直线与平滑曲线效果 自定义点的显示类型与大小 自适应点的数值显示位置 根据指定锚点…

如何讲好ppt演讲技巧(4篇)

如何讲好ppt演讲技巧&#xff08;4篇&#xff09; 如何讲好PPT演讲技巧&#xff08;四篇&#xff09; **篇&#xff1a;精心准备&#xff0c;奠定演讲基础 一个成功的PPT演讲&#xff0c;离不开精心的准备。首先&#xff0c;要确定演讲的主题和目标&#xff0c;确保演讲内容清…

字典及GitHub字典爬取工具

红队API接口Fuzz字典可以用于WEB安全&#xff0c;渗透测试&#xff0c;SRC等场景 完整文件已上传知识星球&#xff0c;需要的朋友可加入查看。

Docker容器---Harbor私有仓库部署与管理

一、搭建本地私有仓库 1、下载registry镜像 [rootlocalhost ~]#docker pull registry Using default tag: latest latest: Pulling from library/registry 79e9f2f55bf5: Pull complete 0d96da54f60b: Pull complete 5b27040df4a2: Pull complete e2ead8259a04: Pull comp…

OPPO Reno10Pro/Reno11/K10手机强解BL刷root权限KSU内核抓包刷机救砖

OPPO Reno10Pro/Reno11/K10手机虽然发布时间并不久&#xff0c;但由于天玑处理器的体质&#xff0c;已经支持强制解锁BL了&#xff0c;该漏洞来自第三方工具适配&#xff0c;支持OPPO天机8100/8200刷机救砖解锁BL不需要等待官方深度测试直接实现。解锁BL后的OPPO Reno10Pro/Ren…

Android binder死亡通知机制

在Andorid 的binder系统中&#xff0c;当Bn端由于种种原因死亡时&#xff0c;需要通知Bp端&#xff0c;Bp端感知Bn端死亡后&#xff0c;做相应的处理。 使用 Bp需要先注册一个死亡通知&#xff0c;当Bn端死亡时&#xff0c;回调到Bp端。 1&#xff0c;java代码注册死亡通知 …

hadoop学习---基于hive的航空公司客户价值的LRFCM模型案例

案例需求&#xff1a; RFM模型的复习 在客户分类中&#xff0c;RFM模型是一个经典的分类模型&#xff0c;模型利用通用交易环节中最核心的三个维度——最近消费(Recency)、消费频率(Frequency)、消费金额(Monetary)细分客户群体&#xff0c;从而分析不同群体的客户价值。在某些…