【C++】map详解

JhonKI 2024-10-12 15:35:04 阅读 77

📢博客主页:https://blog.csdn.net/2301_779549673

📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!

📢本文由 JohnKi 原创,首发于 CSDN🙉

📢未来很长,值得我们全力奔赴更美好的生活✨

在这里插入图片描述

在这里插入图片描述

文章目录

📢前言🏳️‍🌈一、pair类型介绍🏳️‍🌈二、map的增删查🏳️‍🌈三、map的数据修改🏳️‍🌈四、使用样例🏳️‍🌈五、multimap和map的差异👥总结


📢前言

map的结构和set很类似,部分功能就不演示了,上一篇博客中有

在这里插入图片描述

<code>Map 是 C++ 中非常重要的关联容器之一。它以键值对的形式存储数据,其中每个键都是唯一的,这意味着不能有重复的键。如果尝试插入一个已存在的键,将会覆盖该键对应的值。

Map 的内部结构是红黑树,这使得它具有很多优点。首先,数据是有序的,这有助于高效地进行查找、插入和删除操作。查找、插入、删除的平均和最坏时间复杂度都是 O (log n),其中 n 是 map 中元素的个数。

例如,当我们需要存储学生的学号和姓名时,可以使用 map<int, string>,其中学号作为键,姓名作为值。这样可以通过学号快速地找到对应的学生姓名。

Map 的键和值可以是任意类型,但需要注意的是,如果键是自定义类型,需要为该类型提供小于运算符的重载,以便 map 能够对键进行排序。

在实际应用中,Map 非常适合用于需要快速查找和存储键值对的场景。比如,在字典应用中,可以将单词作为键,释义作为值,方便用户快速查询单词的含义。

总之,Map 以其高效的查找、插入和删除操作,以及有序性和唯一键的特点,在 C++ 编程中有着广泛的应用。


🏳️‍🌈一、pair类型介绍

在 C++ 中,pair类是一种模板类型,定义在<utility>头文件中。它将两个不同类型的值组合成一个单一的对象。对于map容器来说,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) );

}

🏳️‍🌈二、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。

Member types

key_type -> The first template parameter (Key)

mapped_type -> The second template parameter (T)

value_type -> pair<const key_type,mapped_type>

// 查找k,返回k所在的迭代器,没有找到返回end(),如果找到了通过iterator可以修改key对应的

mapped_type值

iterator find (const key_type& k);

// ⽂档中对insert返回值的说明

// The single element versions (1) return a pair, with its member pair::first

//set to an iterator pointing to either the newly inserted element or to the

//element with an equivalent key in the map. The pair::second element in the pair

//is set to true if a new element was inserted or false if an equivalent key

//already existed.

// insert插⼊⼀个pair<key, T>对象

// 1、如果key已经在map中,插⼊失败,则返回⼀个pair<iterator,bool>对象,返回pair对象

//first是key所在结点的迭代器,second是false

// 2、如果key不在在map中,插⼊成功,则返回⼀个pair<iterator,bool>对象,返回pair对象

//first是新插⼊key所在结点的迭代器,second是true

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

//代器

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

//operator[]

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

//⼀个是insert返回值pair<iterator,bool>

pair<iterator,bool> insert (const value_type& val);

mapped_type& operator[] (const key_type& k);

// 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;

}

🏳️‍🌈四、使用样例

构造遍历及增删查使用样例

#include<iostream>

#include<map>

using namespace std;

int main() {

// initializer_list构造及迭代遍历

map<string, string> dict = { { "left", "左边"}, { "right", "右边"},

{ "insert", "插⼊"}, { "string", "字符串" }

};

//map<string, string>::iterator it = dict.begin();

auto it = dict.begin();

while (it != dict.end()) {

//cout << (*it).first <<":"<<(*it).second << endl;

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

// 第⼀个->是迭代器运算符重载,返回pair*,第⼆个箭头是结构指针解引⽤取

//pair数据

//cout << it.operator->()->first << ":" << it.operator->()-> second << endl;

cout << it->first << ":" << it->second << endl;

++it;

}

cout << endl;

// insert插⼊pair对象的4种⽅式,对⽐之下,最后⼀种最⽅便

pair<string, string> kv1("first", "第⼀个");

dict.insert(kv1);

dict.insert(pair<string, string>("second", "第⼆个"));

dict.insert(make_pair("sort", "排序"));

dict.insert({ "auto", "⾃动的" });

// "left"已经存在,插⼊失败

dict.insert({ "left", "左边,剩余" });

// 范围for遍历

for (const auto& e : dict) {

cout << e.first << ":" << e.second << endl;

}

cout << endl;

string str;

while (cin >> str) {

auto ret = dict.find(str);

if (ret != dict.end()) {

cout << "->" << ret->second << endl;

} else {

cout << "⽆此单词,请重新输⼊" << endl;

}

} // erase等接⼝跟set完全类似,这⾥就不演⽰讲解了

return 0;

}

map的迭代器和[]功能样例:

#include<iostream>

#include<map>

#include<string>

using namespace std;

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;

}

#include<iostream>

#include<map>

#include<string>

using namespace std;

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;

}

#include<iostream>

#include<map>

#include<string>

using namespace std;

int main() {

map<string, string> dict;

dict.insert(make_pair("sort", "排序"));

// key不存在->插⼊ {"insert", string()}

dict["insert"];

// 插⼊+修改

dict["left"] = "左边";

// 修改

dict["left"] = "左边、剩余";

// key存在->查找

cout << dict["left"] << endl;

return 0;

}

🏳️‍🌈五、multimap和map的差异

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


👥总结


本篇博文对 map 做了一个较为详细的介绍,不知道对你有没有帮助呢

觉得博主写得还不错的三连支持下吧!会继续努力的~

请添加图片描述



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。