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

news2024/10/5 19:12:19
图片名称

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

🎉专栏首页: 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.coloradmin.cn/o/1638379.html

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

相关文章

Word域代码学习(简单使用)-【SEQ】

Word域代码学习(简单使用)-【SEQ】 快捷键 序号快捷键操作1 Ctrl F9 插入域代码花括号2 F9 显示域代码结果3 Shift F9 切换为域代码4 Windows Alt F9 切换全部域代码 域代码说明 域代码不区分大小写在word中&#xff0c;依次选择插入➡文档部件➡域即可选择插入…

WordPress Automatic插件 SQL注入漏洞复现(CVE-2024-27956)

0x01 产品简介 WordPress Automatic(又称为WP Automatic)是一款流行的WordPress插件,旨在帮助网站管理员自动化内容创建和发布。该插件可以从各种来源(如RSS Feeds、社交媒体、视频网站、新闻网站等)获取内容,并将其自动发布到WordPress网站。 0x02 漏洞概述 WordPres…

Linux-进程调度器

1. 前言 在计算机中&#xff0c;进程的数量远多于cpu的数量&#xff0c;所以就存在&#xff0c;多个进程抢占一个cpu的情况&#xff0c;所以就需要一套规则&#xff0c;决定这些进程被处理的顺序&#xff0c;这就叫做进程调度。 在我的简单理解下&#xff0c;其实就是把进程放…

正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-6.4--汇编LED驱动程序

前言&#xff1a; 本文是根据哔哩哔哩网站上“正点原子[第二期]Linux之ARM&#xff08;MX6U&#xff09;裸机篇”视频的学习笔记&#xff0c;在这里会记录下正点原子 I.MX6ULL 开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了正点原子教学视频和链接中的内容。…

网络安全之弱口令与命令爆破(中篇)(技术进阶)

目录 一&#xff0c;什么是弱口令&#xff1f; 二&#xff0c;为什么会产生弱口令呢&#xff1f; 三&#xff0c;字典的生成 四&#xff0c;使用Burpsuite工具验证码爆破 总结 笔记改错 一&#xff0c;什么是弱口令&#xff1f; 弱口令就是容易被人们所能猜到的密码呗&a…

Android数据恢复软件快速比较:Android数据恢复的7最佳工具

您在 Android 设备上保留哪些类型的数据&#xff1f;如果您和大多数人一样&#xff0c;那么您可能已经列出了文档、照片、视频和音频文件。如果您使用智能手机或平板电脑的时间足够长&#xff0c;我们愿意打赌您拥有Android数据丢失的第一手经验。 幸运的是&#xff0c;我们也…

JSON教程(非常详细)

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

Python | Leetcode Python题解之第62题不同路径

题目&#xff1a; 题解&#xff1a; class Solution:def uniquePaths(self, m: int, n: int) -> int:return comb(m n - 2, n - 1)

基于SSM的个人博客系统(三)

目录 第五章 系统实现 5.1 登录模块 5.1.1 博主登录 5.2 博客管理模块&#xff1a; 5.2.1 博客查询 5.2.2 博客新建 5.2.3 博客修改 5.2.4 博客删除 5.3 博客类别管理模块 前面内容请移步 基于SSM的个人博客系统&#xff08;二&#xff09; 个人博客系统的设计…

飞腾FT1500A-16 6U VPX高性能密集计算刀片

飞腾FT1500A-16 6U VPX高性能密集计算刀片 一款高性能6U VPX单板显示计算机&#xff0c;产品遵循OpenVPX Vita65规范。采用目前主流的GPGPU架构&#xff0c;其板载一颗天津飞腾公司的FT1500A-4四核处理器&#xff0c;并搭配AMD?公司的高性能嵌入式显卡Radeon? E8860 GPU。E88…

【论文阅读:Data Shapley: Equitable Valuation of Data for Machine Learning】

1.基于蒙特卡罗方法和截断的方法计算&#xff08;TMC—Shapley&#xff09; 输入 训练数据集D学习算法A表现分V 输出 各方数据的贡献 ϕ i \phi_i ϕi​ 初始化 初始化各方的贡献 ϕ i 0 \phi_i0 ϕi​0,并设置当前迭代轮次为0 过程 算法进入一个循环迭代&#xff0c;直到满足…

Vue 之 在当前页面的实现分页效果

目录 场景实现 场景 假设&#xff0c;我们现在有这么一个需求&#xff1a; 上述图片的空白内容是活动的&#xff0c;由下面的两个按钮控制上一页、下一页&#xff1b;我们应该可以怎么去实现&#xff1f; 实现 思路&#xff1a; 其实这个问题&#xff0c;我们仿照其他的UI框…

ctf web-部分

** web基础知识 ** *一.反序列化 在PHP中&#xff0c;反序列化通常是指将序列化后的字节转换回原始的PHP对象或数据结构的过程。PHP中的序列化和反序列化通过serialize()和unserialize()函数实现。 1.序列化serialize() 序列化说通俗点就是把一个对象变成可以传输的字符串…

2024年五一数学建模C题完整解题思路代码

2024年第二十一届五一数学建模竞赛题目 C题 煤矿深部开采冲击地压危险预测 煤炭是中国的主要能源和重要的工业原料。然而&#xff0c;随着开采深度的增加&#xff0c;地应力增大&#xff0c;井下煤岩动力灾害风险越来越大&#xff0c;严重影响着煤矿的安全高效开采。在各类深…

如何快速搭建nginx服务

华子目录 nginx简介概念特点nginx框架nginx关键工作机制 nginx正向代理功能nginx反向代理功能nginx反向代理的工作流程代理本质 nginx负载均衡部署nginx常用命令systemctl系列nginx自带命令 nginx配置文件主配置文件/etc/nginx/nginx.conf内容结构模块分析配置分析注意示例 ngi…

常见公式的几何解释

本文旨在深入探讨常见数学公式的几何意义&#xff0c;通过直观的图形和解释&#xff0c;帮助读者更好地理解并掌握这些公式的本质。文章首先概述了公式与几何图形之间的紧密联系&#xff0c;然后选取了几个典型的数学公式&#xff0c;进行详细解析。每个公式都将配以相应的几何…

Zynq 7000 系列之启动模式—JTAG启动

JTAG Boot&#xff08;JTAG启动&#xff09;是一种使用JTAG接口来启动设备的方法。JTAG&#xff08;Joint Test Action Group&#xff09;是一种国际标准测试协议&#xff0c;最初用于对芯片进行测试&#xff0c;现在已广泛应用于各种设备的调试和启动过程。在JTAG Boot过程中&…

是机遇?是未来?拥抱 AI Agent ,拥抱 AI 2.0时代~

✍️ 作者&#xff1a;哈哥撩编程&#xff08;视频号同名&#xff09; 博客专家全国博客之星第四名超级个体COC上海社区主理人特约讲师谷歌亚马逊演讲嘉宾科技博主极星会首批签约作者 &#x1f3c6; 推荐专栏&#xff1a; &#x1f3c5; 程序员&#xff1a;职场关键角色通识宝…

社交媒体数据恢复:飞机 X

飞机 X数据恢复方法 在本文中&#xff0c;我们将探讨如何在不使用特定数据恢复软件的情况下尝试恢复飞机 X聊天记录和文件。请注意&#xff0c;这些方法并不保证100%的成功率&#xff0c;但它们可以在一定程度上帮助您恢复丢失的数据。 1. 检查最近的备份 如果您启用了飞机 …

【17-模型选择与调优:交叉验证和网格搜索在Scikit-learn中的实践】

文章目录 前言交叉验证:保证模型的稳健性理论基础Scikit-learn中的实现网格搜索:寻找最佳参数理论基础Scikit-learn中的实现应用示例结论前言 模型选择和调优是机器学习项目成功的关键步骤。在Scikit-learn中,交叉验证和网格搜索是两个强大的工具,用于选择最佳模型和调整参…