map的使用

pair类型介绍

map底层的红⿊树节点中的数据,使⽤pair<Key, T>存储键值对数据
typedef pair<const Key, T> value_type;
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){}template<class U, class V>pair(const pair<U, V>& pr) : first(pr.first), second(pr.second){}
};
template <class T1, class T2>
inline pair<T1, T2> make_pair(T1 x, T2 y)
{return (pair<T1, T2>(x, y));
}

可以理解为,现在的key和value不再单独出现,而是整合在一个pair的结构体里面,pair里有两个成员变量,一个代表key,另一个代表value。 

使用样例:

int main()
{map<string, string> dict;//1.插入有名的pair对象pair<string, string> kv1("first", "第一个");dict.insert(kv1);//2.插入匿名的pair对象dict.insert(pair<string, string>("second", "第二个"));//3.调用make_pair:本质是一个函数模板(不用自己实例化模板参数),返回一个pair对象(常用)//与2.等价dict.insert(make_pair("sort", "排序"));//补充注意点:因为map不允许数据冗余,当再执行插入一个key同为sort的代码,并不会修改原有数据,因为map底层红黑树比较的基准永远都是key,与value无关dict.insert(make_pair("sort", "排序mmmmm"));//C++11之后允许多参数的隐式类型转换--->//4.使用{},隐式类型转换成pair(最爱用)dict.insert({ "auto","自动的" });//initializer_list构造//内层走隐式类型转换,外层走initializermap<string, string> dict = { {"left", "左边"}, {"right", "右边"}, {"insert", "插⼊"},{ "string", "字符串" } };return 0;
}

C++不支持同时返回两个值,如果要同时返回两个值,就需要用一个结构来封装(将key和value放在一个pair结构里面),而且pair不支持流插入和流提取 

因为对于一个节点数据来说,节点包含了对应的key和value数据,因此,访问数据应该使用下列两种方式操作:

  1. *it是一个pair,可以用"  . 对象  "的方式进行访问
  2. 迭代器除了重载 operator* ,还重载了 operator-> ,如果迭代器指向的对象是一个结构的时候就使用 -> 
	//打印数据auto it = dict.begin();while (it != dict.end()){//C++不支持同时返回两个值//cout << *it << endl;//*it是一个pair,可以用"  . 对象  "的方式进行访问cout << (*it).first << ' : ' << (*it).second << endl;//迭代器除了重载 operator* ,还重载了 operator-> ,如果迭代器指向的对象是一个结构的时候就使用 -> cout << it->first << ' : ' << it->second << endl;//本源是://it.operator->()->first;//it.operator->():返回数据的指针:也就是pair*//pair*->firstit++;}cout << endl;return 0;
}

map的使用

map其实跟set相似,只是map多了一个value值(second)

具体参考(点击进入)

  • map的声明如下,Key就是map底层关键字的类型,T是map底层value的类型,set默认要求Key⽀持⼩于⽐较,如果不⽀持或者需要的话可以⾃⾏实现仿函数传给第⼆个模版参数;(注意是以Key为比较基准
  • map底层存储数据的内存是从空间配置器申请的。⼀般情况下,我们都不需要传后两个模版参数;
  • map底层是⽤红⿊树实现,增删查(对于修改操作,仅仅支持对value进行修改)效率是 O(logN) ,迭代器遍历是⾛的中序,所以是按key有序顺序遍历的;
  • map的insert操作跟key和value有关,erase/find/count/lower_bound/upper_bound等操作只跟key有关,与value无关;
  • equal_range是找到指定数据的左闭右开区间,返回的是一个pair,多用于multi版本的set/map,来查找相同数据的所在的左闭右开区间。
template < class Key,                                     // map::key_typeclass T,                                       // map::mapped_typeclass Compare = less<Key>,                     // map::key_compareclass Alloc = allocator<pair<const Key, T> >    // map::allocator_type
> class map;

map的构造

map的⽀持正向和反向迭代遍历,遍历默认按key的升序顺序,因为底层是⼆叉搜索树,迭代器遍历⾛的中序;⽀持迭代器就意味着⽀持范围for,map⽀持修改value数据,不⽀持修改key数据,修改关键字数据,破坏了底层搜索树的结构。

// empty (1) ⽆参默认构造
explicit map(const key_compare& comp = key_compare(),const allocator_type& alloc = allocator_type());// range (2) 迭代器区间构造
template <class InputIterator>
map(InputIterator first, InputIterator last,const key_compare& comp = key_compare(),const allocator_type & = allocator_type());// copy (3) 拷⻉构造
map(const map& x);// initializer list (5) initializer 列表构造
map(initializer_list<value_type> il,const key_compare& comp = key_compare(),const allocator_type& alloc = allocator_type());// 迭代器是⼀个双向迭代器
iterator->a bidirectional iterator to const value_type// 正向迭代器
iterator begin();
iterator end();// 反向迭代器
reverse_iterator rbegin();
reverse_iterator rend();

 map的增删查

map的增删查关注以下⼏个接⼝即可:

map增接⼝,插⼊的pair键值对数据,跟set所有不同,但是查和删的接⼝只⽤关键字key跟set是完全类似的,不过find返回iterator,不仅仅可以确认key在不在,还找到key映射的value,同时通过迭代还可以修改value

Member types
key_type->The first template parameter(Key)
mapped_type->The second template parameter(T)
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);// 查找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);// 返回⼤于等k位置的迭代器
iterator lower_bound(const key_type& k);
// 返回⼤于k位置的迭代器
const_iterator lower_bound(const key_type& k) const;

map的数据修改 

  • 前⾯我提到map⽀持修改mapped_type 数据,不⽀持修改key数据,修改关键字数据,破坏了底层搜索树的结构。
  • map第⼀个⽀持修改的⽅式时通过迭代器,迭代器遍历时或者find返回key所在的iterator修改,map还有⼀个⾮常重要的修改接⼝operator[],但是operator[]不仅仅⽀持修改,还⽀持插⼊数据和查找数据,所以他是⼀个多功能复合接⼝
  • 需要注意从内部实现⻆度,map这⾥把我们传统说的value值,给的是T类型,typedef为 mapped_type。⽽value_type是红⿊树结点中存储的pair键值对值。⽇常使⽤我们还是习惯将这⾥的T映射值叫做value。

利⽤find和iterator修改功能,统计⽔果出现的次数:

int main()
{// 利⽤find和iterator修改功能,统计⽔果出现的次数string arr[] = { "苹果", "西⽠", "苹果", "西⽠", "苹果", "苹果", "西⽠","苹果", "⾹蕉", "苹果", "⾹蕉" };map<string, int> countMap;for (const auto& str : arr){// 先查找⽔果在不在map中// 1、不在,说明⽔果第⼀次出现,则插⼊{⽔果, 1}// 2、在,则查找到的节点中⽔果对应的次数++auto ret = countMap.find(str);if (ret == countMap.end()){countMap.insert({ str, 1 });}else{ret->second++;}}for (const auto& e : countMap){cout << e.first << ":" << e.second << endl;}cout << endl;return 0;
}

但是在C++中,并不常以这种方式进行统计次数,而是使用operator [ ] 来实现次数的统计:

对于map重载的operator [ ] ,不再是单纯的实现像数组一样的通过下标去访问元素,这里是说:你给我map里的key,我返回对应的value

mapped_type& operator[] (const key_type& k);
//这里的mapped_type就是我们所指的value
//而他文档写的value_type是我们所指的pair

map重载的operator [ ] 不仅有查找及其修改的功能,它还兼具插入的功能:

  • 当你使用 operator[] 访问 map 中的元素时,如果该元素存在,它会返回一个引用,允许你修改该元素的值。
  • 如果该元素不存在,operator[] 会插入一个新的元素,并将该元素的值初始化为该键类型的默认值。(对于其他语言来说,或者会抛异常)

功能样例:

int main()
{map<string, string> dict;dict.insert({ "sort","排序" });//key不存在->插入{"insert",string()};dict["insert"];//这时候【】的作用就是进行插入操作//key不存在->插入+修改dict["tea"] = "茶";//key存在->修改dict["insert"] = "插入";//查找:一定要确保所查找的数据存在,不然就是变成了key不存在的插入行为了//应当谨慎使用其查找功能cout << dict["tea"] << endl;for (auto d : dict){cout << d.first << ":" << d.second << endl;}cout << endl;return 0;
}

其插入操作,等价于底层调用:

//A call to this function is equivalent to:
(*((this->insert(make_pair(k,mapped_type()))).first)).second

到这里,我们需要好好理解map中的insert操作的返回值:

穿插:map中insert的返回值
//single element (1)	
pair<iterator,bool> insert (const value_type& val);

可以看出,这里的insert的返回值是一个pair,其中value_type也是一个pair(用于存键值对),因此,有两个pair;

  1. 返回的pair中iterator(迭代器)作用是为了返回新插入元素位置的迭代器,如该map结构已经存在相同元素,则指向原有元素位置的迭代器
  2. 返回的pair中bool的作用是判断insert操作是否成功,如果key存在,该布尔值为false,反之,为true

简单来说:

插入成功:
pair<新插入值所在迭代器, true>

插入失败:
pair<已经存在的跟key相等值迭代器, false>

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

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

需要注意的是这⾥有两个pair,不要混淆了,⼀个是map底层红⿊树节点中存的pair<key, T>,另
⼀个是insert返回值pair<iterator,bool>

operator[] 的内部实现:

// operator的内部实现
mapped_type& operator[] (const key_type& k)
{// 1、如果k不在map中,insert会插⼊k和mapped_type默认值,同时[]返回结点中存储mapped_type值的引⽤,那么我们可以通过引⽤修改返映射值。所以[]具备了插⼊ + 修改功能// 2、如果k在map中,insert会插⼊失败,但是insert返回pair对象的first是指向key结点的迭代器,返回值同时[]返回结点中存储mapped_type值的引⽤,所以[]具备了查找 + 修改的功能pair<iterator, bool> ret = insert({ k, mapped_type() });iterator it = ret.first;return it->second;
}

代码优化:使用operator [ ] 

int main()
{// 利⽤[]插⼊+修改功能,巧妙实现统计⽔果出现的次数string arr[] = { "苹果", "西⽠", "苹果", "西⽠", "苹果", "苹果", "西⽠","苹果", "⾹蕉", "苹果", "⾹蕉" };map<string, int> countMap;for (const auto& str : arr){// []先查找⽔果在不在map中// 1、不在,说明⽔果第⼀次出现,则插⼊{⽔果, 0},同时返回次数的引⽤,++⼀下就变成1次了// 2、在,则返回⽔果对应的次数++countMap[str]++;}for (const auto& e : countMap){cout << e.first << ":" << e.second << endl;}cout << endl;return 0;
}

multimap和map的区别

在C++标准库中,mapmultimap都是关联容器,它们存储了键值对(key-value pairs),并且提供了基于键的有序访问。它们之间的主要区别在于:

  1. 唯一性

    • map:每个键必须是唯一的。如果尝试插入一个已经存在的键,那么该操作将不会改变容器。
    • multimap:允许有多个元素具有相同的键。这意味着可以存储多个具有相同键的值。
  2. 插入操作

    • map:如果插入的键已经存在,那么插入操作不会成功,容器内容不变。
    • multimap:即使键已经存在,插入操作也会成功,并且会创建一个新的键值对。
  3. 迭代器

    • map:迭代器指向唯一的元素,所以迭代器的 operator* 直接返回一个 pair 对象,其中包含键和值。
    • multimap:迭代器也指向 pair 对象,但由于可能有多个相同的键,所以迭代器的 operator* 返回一个引用到 pair 的对象。
  4. 查找操作

    • map:查找操作返回一个迭代器,指向找到的元素或者在未找到时指向插入点。
    • multimap:查找操作返回一个迭代器范围(一个迭代器对),包含所有具有指定键的元素。
  5. 删除操作

    • map:删除操作会删除键及其关联的值。
    • multimap:删除操作会删除所有具有该键的元素。
  6. 成员函数

    • map:提供了 findcountequal_range 等成员函数。
    • multimap:提供了 findcountequal_range 等成员函数,但 count 返回的是具有相同键的元素数量,equal_range 返回一个迭代器对,表示具有相同键的范围
  7. 用途

    • map:当你需要快速查找、插入和删除唯一键时使用。
    • multimap:当你需要关联多个具有相同键的值时使用。

在C++标准库中,multimap不支持使用 operator[] 的原因是因为 multimap 允许有多个元素具有相同的键。operator[] 通常用于访问元素,并且它返回一个引用,这意味着它应该定位到一个确切的元素。

对于 map,由于每个键是唯一的,使用 operator[] 可以直接访问或创建并访问一个特定的键值对。

然而,对于 multimap,如果使用 operator[] 会遇到以下问题:

  1. 歧义性:如果有多个元素具有相同的键,operator[] 应该返回哪一个元素的引用?
  2. 效率问题operator[] 通常需要提供一个快速访问,但 multimap 需要遍历以找到正确的元素,这与 operator[] 设计的初衷不符。

如何在 multimap 中访问元素

虽然 multimap 不支持 operator[],但可以使用其他方法来访问或修改具有特定键的元素:

  1. 使用 find 方法

    • find 方法返回一个指向找到元素的迭代器,如果元素不存在,则返回 end() 迭代器。
    std::multimap<int, std::string> myMultimap;
    myMultimap.insert(std::make_pair(1, "One"));
    myMultimap.insert(std::make_pair(2, "Two"));
    myMultimap.insert(std::make_pair(2, "Another Two"));auto it = myMultimap.find(2);
    if (it != myMultimap.end()) {std::cout << it->second << std::endl; // 输出 "Two"
    }
  2. 使用 equal_range 方法

    • equal_range 方法返回一个迭代器对,表示具有指定键的所有元素的范围。
    std::pair<std::multimap<int, std::string>::iterator,std::multimap<int, std::string>::iterator> range;range = myMultimap.equal_range(2);
    for (auto it = range.first; it != range.second; ++it) {std::cout << it->second << std::endl; // 输出所有键为2的元素
    }
  3. 使用 lower_boundupper_bound 方法

    • 这些方法也可以用来找到具有特定键的元素的范围。
    auto lower = myMultimap.lower_bound(2);
    auto upper = myMultimap.upper_bound(2);
    for (auto it = lower; it != upper; ++it) {std::cout << it->second << std::endl; // 输出所有键为2的元素
    }

总结:

multimap 不支持 operator[] 是因为它的设计允许多个具有相同键的元素存在,这会导致访问时的歧义性。相反,可以使用 findequal_rangelower_boundupper_bound 等方法来访问或修改具有特定键的元素。这些方法提供了更灵活的访问方式,可以处理 multimap 中键的重复性。

习题巩固:

学习本章之后,可以通过下面习题进行巩固知识 

138. 随机链表的复制 - ⼒扣(LeetCode)
692. 前K个⾼频单词 - ⼒扣(LeetCode)

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

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

相关文章

Unity的Text组件中实现输入内容的渐变色效果

要在Unity的Text组件中实现输入内容的渐变色效果&#xff0c;默认的Text组件不直接支持渐变色。但是&#xff0c;你可以通过以下几种方式实现&#xff1a; ### 1. **使用Shader**来实现渐变效果 通过自定义Shader为Text组件创建一个渐变效果。这是一个常用的做法&#xff0…

MySQL---创建数据库(基于SQLyog)

目录 0.前言 1.基本认识 1.1编码集 1.2检验规则 2.库的创建和销毁 2.1指令介绍 2.2你可能会出现的问题 3.查看数据库属性 4.创建指定数据库 5.创建表操作 0.前言 之前写过一篇这个关于表的创建和销毁的操作&#xff0c;但是当时是第一次学习&#xff0c;肯定有些地方…

初识 C 语言(一)

目录 一、 第一个 C 程序1. printf() 函数和 stdio.h 头文件2. main() 函数和 return 语句 二、类型和变量1. C 语言中的基本类型2. 变量的创建和命名规则3. 类型和变量的大小 三、printf() 函数和 scanf() 函数1. printf() 函数的使用2. 各种类型的输出格式3. scanf() 函数的使…

2. 网络模型、协议

网络模型、协议 一、OSI七层模型1、OSI七层作用2、数据封装、解封装 二、典型的协议1、应用层2、传输层2.1 TCP建立连接&#xff0c; 三次握手2.2 断开连接&#xff0c;四次挥手 3、网络层 一、OSI七层模型 Open System Interconnect 开放式系统互连模型 降低数据在网络中传输…

web基础—dvwa靶场(十一)CSP Bypass

CSP Bypass(CSP 绕过) 内容安全策略&#xff08;CSP&#xff09;用于定义脚本和其他资源可以从何处加载或执行&#xff0c;本模块将指导您根据开发人员犯下的常见错误来绕过该策略。 这些漏洞都不是 CSP 中的实际漏洞&#xff0c;它们都是实现 CSP 的方式中的漏洞。 绕过内容安…

智慧城市主要运营模式分析

(一)运营模式演变 作为新一代信息化技术落地应用的新事物,智慧城市在建设模式方面借鉴了大量工程建设的经验,如平行发包(DBB,Design-Bid-Build)、EPC工程总承包、PPP等模式等,这些模式在不同的发展阶段和条件下发挥了重要作用。 在智慧城市发展模式从政府主导、以建为主、…

Eigen之SelfAdjointEigenSolver

Eigen::SelfAdjointEigenSolver 是 Eigen 库中的一个类,用于计算自伴随矩阵(对称矩阵)的特征值和特征向量。自伴随矩阵是指其等于自身的共轭转置的矩阵,通常在物理和工程中出现,比如协方差矩阵、赫尔米特矩阵等。 常用用法: 计算特征值和特征向量: SelfAdjointEigenSol…

计算机毕业设计 基于Flask+Vue的博客系统 Python毕业设计 前后端分离 附源码 讲解 文档

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

Linux嵌入式驱动开发指南(速记版)---Linux基础篇

第一章 Ubuntu系统入门 uname -a #查看内核版本 cat etc/issue #查看系统版本 1.1 Linux磁盘管理 1.1.1 Linux磁盘管理基本概念 关键词&#xff1a; Linux 磁盘管理 挂载点 /etc/fstab文件 分区 ls /dev/sd* 联系描述&#xff1a; Linux 磁盘管理体系通过“挂载点”概念替代…

[数据集][目标检测]手机识别检测数据集VOC+YOLO格式9997张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;9997 标注数量(xml文件个数)&#xff1a;9997 标注数量(txt文件个数)&#xff1a;9997 标注…

linux之mysql安装

1:mysql安装包下载 下载地址 可私信我直接获取安装包 2:linux下wget命令下载 下载地址 wget https://dev.mysql.com/get/Downloads/MySQL-5.7/mysql-5.7.24-linux-glibc2.12-x86_64.tar.gz3:手动安装 将自己的安装包上传到对应的位置 解压 压缩包 使用命令 tar -zxvf mysql-5.7…

Mac优化清理工具CleanMyMac X 4.15.6 for mac中文版

CleanMyMac X 4.15.6 for mac中文版下载是一款功能更加强大的系统优化清理工具&#xff0c;软件只需两个简单步骤就可以把系统里那些乱七八糟的无用文件统统清理掉&#xff0c;节省宝贵的磁盘空间。CleanMyMac X 4.15.6 for mac 软件与最新macOS系统更加兼容&#xff0c;流畅地…

华为静态路由(route-static)

静态路由的组成 在华为路由器中&#xff0c;使用ip route-static命令配置静态路由。 一条静态路由主要包含以下要素&#xff1a; 目的地址&#xff1a;数据包要到达的目标IP地址 子网掩码&#xff1a;用于指定目的地址的网络部分和主机部分 下一跳地址&#xff08;可选&#…

中国蚁剑(antSword)安装使用

antSword下载 antSword-Loader下载 作者&#xff1a;程序那点事儿 日期&#xff1a;2024/09/12 19:35 中国蚁剑&#xff08;AntSword&#xff09;是一款跨平台的开源网站管理工具&#xff0c;旨在满足渗透测试人员的需求。它是一个功能强大的工具&#xff0c;可以帮助用户管理…

基于单片机的智能温控风扇系统的设计

[摘 要] 设计一种基于单片机的智能温控风扇系统,系统由 STC 系列的 51 单片机 、 温度传感器 、 LED 数码管和风扇等模块组成。 本系统具有对外界温度感知以及对感知数据进行分析处理 、 智能调节等功能,避免因温度过高而产生对整个系统的损坏,以此提高整个系统的性能…

如何使用numpy反转数组

如何使用numpy反转数组 1、使用np.flip()函数 可以使用flip(m, axisNone)函数来对数组进行反转&#xff1a; m:输入数组 axis:为None则行列都反转 axis:为0则反转行 axis:为1则反转列2、代码 import numpy as np# 创建一维数组 arr np.array([[1, 2, 3, 4, 5],[2, 2, 3, 4…

想学习下Python和深度学习,Python需要学习到什么程度呢?

想要学习Python和深度学习&#xff0c;Python的学习程度需要达到能够熟练运用这门语言进行编程&#xff0c;并能够理解和实现深度学习模型的基本构建和训练过程。以下是一些推荐的书籍&#xff0c;可以帮助你系统地学习Python和深度学习&#xff1a; Python学习推荐书籍 《Py…

K8S精进之路-控制器StatefulSet有状态控制 -(2)

状态说明 在进行StatefulSet部署之前&#xff0c;我们首先可能要了解一下&#xff0c;什么是"有状态应用"和"无状态应用"。无状态应用就是pod无论部署在哪里&#xff0c;在哪台服务器上提供服务&#xff0c;都是一样的结果&#xff0c;比如经常用的nginx。…

交换基础【计算机网络】

交换基础 1、交换机的工作原理有哪4项操作&#xff0c;地址表如何建立的&#xff1f; 4项基本操作 丢弃 当本端口下的主机访问已知本端口下的主机时丢弃 转发 当某端口下的主机访问已知某端口下的主机时转发 扩散 当某端口下的主机访问未知端口下的主机时要扩散 广播 当某…

智能养殖场人机交互检测系统源码分享

智能养殖场人机交互检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Co…