当前位置: 首页 > news >正文

【C++】map 容器的使用

文章目录

  • 上文链接
  • 一、参考文档
  • 二、pair 类型
    • 1. pair 介绍
    • 2. 创建与初始化
    • 3. 访问成员
  • 三、map 介绍
  • 四、map 构造函数与迭代器
    • 1. 无参默认构造
    • 2. 列表构造
    • 3. 迭代器区间构造
    • 4. 拷贝构造
    • 5. 迭代器
  • 五、map 的增删查改
    • 1. insert 插入数据
    • 2. erase 删除数据
    • 3. find 与 count
    • 4. lower_bound 与 upper_bound
    • 5. [ ] 实现插入与修改
  • 六、multimap 与 map 的区别

上文链接

  • 【C++】set 容器的使用

一、参考文档

  • map 与 multimap 的使用

二、pair 类型

1. pair 介绍

pair 是 STL 中一个重要的模板类,它可以用于将两个不同类型的值组合成一个单一对象。它定义在头文件 <utility> 中(<map> 等头文件会间接包含它)。

有的时候函数想要返回两个值的时候,我们就可以把这两个值放在一个 pair 中储存并返回。pair 的底层是一个结构体 struct,其包含两个数据成员:

  • first:pair 中的第一个数据。
  • second:pair 中的第二个数据。

这两个成员是公有的,可直接访问和修改。并且这两个数据的类型可以是任意的,比如:pair<int, string>pair<float, vector<bool>> 等。


2. 创建与初始化

// 方式1:默认构造函数(值初始化)
std::pair<int, double> p1;  // p1.first=0, p1.second=0.0// 方式2:直接初始化
std::pair<std::string, int> p2("Alice", 25);// 方式3:使用 make_pair(自动类型推导)
auto p3 = std::make_pair(3.14, "Pi");// 方式4:C++11统一初始化
std::pair<int, char> p4 = { 65, 'A' };

3. 访问成员

std::pair<int, std::string> student(101, "Bob");// 直接访问公有成员
int id = student.first;             // 101
std::string name = student.second;  // "Bob"// 修改值
student.second = "Robert"; 

三、map 介绍

请添加图片描述

STL 中的 map 容器是一个非常重要的关联式容器,它以键值对 (key-value) 的形式存储元素。并且它的底层是一棵红黑树,也就是一个平衡的二叉搜索树,内部的默认比较规则是按照 (key) 的升序进行的。

map 中存储的每一个键值对是一个 pair 类型:std::pair<const Key, T>Key 是键的类型,T 是值的类型。比如:<"apple", 苹果><“banana”, “香蕉”>

除此之外,map 中的每一个键只能有一个,如果一个键已经存在了,再插入一个相同的键就会失败。


四、map 构造函数与迭代器

注:下面是 map 类中一些类型的原型

key_type -> The first template parameter (Key)
mapped_type -> The second template parameter (T)
value_type -> pair<const key_type,mapped_type>

1. 无参默认构造

// empty 无参默认构造 
explicit map (const key_compare& comp = key_compare(),const allocator_type& alloc = allocator_type());
// 示例
map<string, int> m1;

2. 列表构造

// initializer list 列表构造 
map (initializer_list<value_type> il,
// 示例
map<string, int> score = 
{{"张三", 121},{"李四", 127},{"王五", 120}
};for (auto& e : score2) cout << e.first << ":" << e.second << endl;
// 输出
李四:127
王五:120
张三:121

注:上面的输出结果的顺序是按照键的字典序排升序时候的结果。


3. 迭代器区间构造

// range 迭代器区间构造 
template <class InputIterator>
map (InputIterator first, InputIterator last,const key_compare& comp = key_compare(),const allocator_type& = allocator_type());
// 示例
map<string, int> score = 
{{"张三", 121},{"李四", 127},{"王五", 120}
};map<string, int> score2(score.begin(), score.end());for (auto& e : score2) cout << e.first << ":" << e.second << endl;
// 输出
李四:127
王五:120
张三:121

4. 拷贝构造

// copy 拷⻉构造 
map (const map& x);
// 示例
map<string, double> m1({ { "pi", 3.14 }, {"e", 2.718} });
map<string, double> m2(m1);for (auto& e : score2) cout << e.first << ":" << e.second << endl;
// 输出
e:2.718
pi:3.14

5. 迭代器

// 迭代器是⼀个双向迭代器 
iterator -> a bidirectional iterator to const value_type// 正向迭代器 
iterator begin();
iterator end();// 反向迭代器 
reverse_iterator rbegin();
reverse_iterator rend();

五、map 的增删查改

1. insert 插入数据

  • 单个数据插入,如果 key 已经存在则插入失败,key 存在但 value 不相等也会插入失败。
pair<iterator,bool> insert (const value_type& val);

这个函数的返回值是一个 pair 类型,分为两种情况:

  • 如果插入成功,返回 <插入的该数据对应的迭代器, true>
  • 如果插入失败,返回 <已经存在的该数据对应的迭代器, false>
// 示例
map<string, string> dict = 
{{"left", "左边"}, {"right", "右边"},{"insert", "插入"},{ "string", "字符串" } 
};auto it = dict.begin();
while (it != dict.end())
{// cout << (*it).first << ":"<< (*it).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 (const auto& e : dict) cout << e.first << ":" << e.second << endl;
cout << endl;
// 输出
insert:插入
left:左边
right:右边
string:字符串auto:自动的
first:第一个
insert:插入
left:左边
right:右边
second:第二个
sort:排序
string:字符串

  • 列表插入,已经在容器中存在的不会插入
void insert (initializer_list<value_type> il);
// 示例
map<string, int> inventory = 
{{"Apple", 50},{"Banana", 30}
};inventory.insert(
{{"Orange", 40},{"Grapes", 60}
});

  • 迭代器区间插入,已经在容器中存在的不会插入。
void insert (InputIterator first, InputIterator last);
// 示例
map<string, int> inventory = 
{{"Apple", 50},{"Banana", 30}
};std::map<std::string, int> discountItems = 
{{"Banana", 10},    // 重复键,不会插入{"Watermelon", 5}
};inventory.insert(discountItems.begin(), discountItems.end());

2. erase 删除数据

  • 删除元素 kk 不存在返回 0,存在则返回 1。
size_type erase (const key_type& k);
  • 删除一个迭代器位置的值,返回被删除位置下一个位置的迭代器。
iterator erase (const_iterator position);
  • 删除一段迭代器区间的值。
iterator erase (const_iterator first, const_iterator last);

示例:

map<string, string> dict =
{{"left", "左边"},{"right", "右边"},{"insert", "插入"},{ "string", "字符串" },{ "auto", "自动的" }
};for (const auto& e : dict) cout << e.first << ":" << e.second << endl;
cout << endl;dict.erase("right");
for (const auto& e : dict) cout << e.first << ":" << e.second << endl;
cout << endl;auto it1 = dict.find("string");
dict.erase(it1);
for (const auto& e : dict) cout << e.first << ":" << e.second << endl;
cout << endl;auto it2 = dict.find("insert");
dict.erase(it2, dict.end());
for (const auto& e : dict) cout << e.first << ":" << e.second << endl;
cout << endl;
auto:自动的
insert:插入
left:左边
right:右边
string:字符串auto:自动的
insert:插入
left:左边
string:字符串auto:自动的
insert:插入
left:左边auto:自动的

3. find 与 count

  • find:查找键 k,找到了返回对应的迭代器,没有找到返回 end()
iterator find (const key_type& k);
// 示例
map<string, string> dict =
{{"left", "左边"},{"right", "右边"},{"insert", "插入"},{ "string", "字符串" }
};string s;
cin >> s;auto it = dict.find(s);
if (it != dict.end())
{dict.erase(it);cout << "删除成功" << endl;for (const auto& e : dict) cout << e.first << ":" << e.second << endl;
}
else
{cout << s << "不存在" << endl;
}
// 输入1
right
// 输出1
删除成功
insert:插入
left:左边
string:字符串// 输入2
auto
// 输出2
auto不存在

  • count:查找键 k,返回其个数。
size_type count (const key_type& k) const;

4. lower_bound 与 upper_bound

  • lower_bound:返回大于等于 k 位置的迭代器。
  • upper_bound:返回大于 k 位置的迭代器。
iterator lower_bound (const key_type& k);
const_iterator lower_bound (const key_type& k) const;iterator upper_bound (const key_type& k);
const_iterator upper_bound (const key_type& k) const;
// 示例
map<char, int> m =
{{ 'a', 20 },{ 'b', 15 },{ 'c', 30 },{ 'd', 50 },{ 'e', 40 }
};auto it_l = m.lower_bound('b');
auto it_u = m.upper_bound('d');cout << it_l->first << ":" << it_l->second << endl;
cout << it_u->first << ":" << it_u->second << endl;
// 输出
b:15
e:40

5. [ ] 实现插入与修改

在 map 中是不允许修改 key 的,因为 map 的底层是一棵二叉搜索树,它是按照 key 来进行排序的,修改了 key 就破坏了搜索树的结构。但是我们可以修改 key 对应的 value,如何修改?。

其中一种修改的方式时通过迭代器,用迭代器遍历或者用 find 返回 key 所在的 iterator 进行修改,map 还有一个非常重要的修改接口 operator[],但是 operator[] 不仅可以持修改对应的 value,还支持插入数据和查找数据,所以他是一个多功能复合接口。

map<string, int> price
{{"苹果", 20},{"香蕉", 30},{"橙子", 15},{"西瓜", 35}
};for (const auto& e : price) cout << e.first << ":" << e.second << endl;
cout << endl;// 修改已经存在的数据
price["苹果"] += 5;  // 修改
cout << "苹果:" << price["苹果"] << endl << endl;// 如果不存在,则会自动插入。若没有赋值,则插入后的对应值为该类型的默认值,如: int 为 0,double 为 0.0
price["芒果"];  // 插入
for (const auto& e : price) cout << e.first << ":" << e.second << endl;
cout << endl;price["葡萄"] = 30;  // 插入 + 修改
for (const auto& e : price) cout << e.first << ":" << e.second << endl;
// 输出
橙子:15
苹果:20
西瓜:35
香蕉:30苹果:25橙子:15
芒果:0
苹果:25
西瓜:35
香蕉:30橙子:15
芒果:0
苹果:25
葡萄:30
西瓜:35
香蕉:30

六、multimap 与 map 的区别

multimap 和 map 的使用基本是一样的,主要区别点在于 multimap 支持关键值 key 的重复,那么 insert/find/count/erase 都围绕着支持关键值 key 重复有所差异,这里跟 set 和 multiset 完全一样。比如 find时,有多个 key,返回中序遍历的第一个 key。其次就是 multimap 不支持 []

http://www.dtcms.com/a/353482.html

相关文章:

  • 论文阅读:Gorilla: Large Language Model Connected with Massive APIs
  • Python基础:PyMySQL
  • 音视频开发学习路线梳理(附 GitHub 仓库)
  • 达梦数据库-控制文件 (二)
  • FPGA开发流程
  • 一键搭建开发环境:制作bash shell脚本
  • Apple Silicon Mac 上解决 Docker 平台不匹配和 QEMU 段错误问题
  • 腾讯云服务器重启卡住:原因分析与底层原理详解
  • AI-调查研究-62-机器人 机械臂五大应用场景详解:从焊接到手术,从农田到太空
  • Knife4j 文档展示异常的小坑
  • GBDT(Gradient Boosting Decision Tree,梯度提升决策树)总结梳理
  • 如何设置 Lustre 文件系统并在其上运行 PostgreSQL
  • 设计模式9-责任链模式
  • UDC否定响应码学习
  • 未成功:使用 Nginx 搭建代理服务器(正向代理 HTTPS 网站)
  • 《StarRocks、Doris、ClickHouse 深度对比:三大 OLAP 引擎的优劣与应用场景》
  • go的实现arp客户端
  • 《方法论》--笛卡尔
  • 【学习笔记】非异步安全函数(禁止在信号处理中调用)
  • 雷卯针对香橙派Orange Pi 4开发板防雷防静电方案
  • 【Golang】 项目启动方法
  • Android12 user版本默认开启adb调试
  • centos7安装java mysql redis nginx
  • docker 所有常用命令,配上思维导图,加图表显示
  • 713 乘积小于k的子数组
  • Mysql数据挂载
  • Leetcode+Java+动态规划IV
  • electron离线开发环境变量ELECTRON_OVERRIDE_DIST_PATH
  • [MT6835] MT6835开启secureboot导致写入分区提示Security deny for [customer].
  • 【图像算法 - 25】基于深度学习 YOLOv11 与 OpenCV 实现人员跌倒识别系统(人体姿态估计版本)