C++ 标准库中提供了许多用于处理数据结构的容器和工具。pair 和 map 是两个非常有用的工具,广泛应用于存储和处理关联数据。在本文中,我们将详细介绍 pair 与 map 的相关操作,并结合代码实例为读者提供清晰的理解。
pair:成对数据的存储
1. pair 基本介绍
pair 是 C++ 标准库中的一种数据结构,它允许我们将两个不同类型的数据组合成一对。它的典型用法是用来返回两个相关联的值,例如姓名和电话号码。每个 pair 包含两个值,这两个值分别可以通过 .first 和 .second 访问。
2. pair 的创建与使用
2.1创建 pair 的几种方式:
直接使用构造函数创建:
pair<string, int> p("Tom", 10086);
使用 make_pair 函数创建:
pair<string, int> p2 = make_pair("make", 191919190);
列表初始化(C++11 新特性):
pair<string, int> p3 = { "hhhh", 19298318 };
2.2pair 的常用操作:
访问 pair 中的第一个和第二个值:
cout << "姓名:" << p.first << endl;
cout << "电话:" << p.second << endl;
判断两个 pair 对象是否相等:
if (p != p2)
{
cout << "不相等" << endl;
}
整体操作如下:
//pair(二元组):成对出现的数据,利用对组可以返回两个数据
#include<iostream>
#include<string>
#include <utility> // 包含 pair 对组相关的头文件
using namespace std;
//pair<第一个值类型, 第二个值类型> pr
// 第一个值类型:要储存的第一个值的数据类型
// 第二个值类型:要储存的第二个值的数据类型
// 取第一个值:.first
// 取第二个值:.second
// 判断相等:==, !=
// 测试 pair 的各种构造与操作
void test0()
{
// 使用构造函数直接创建 pair 对象,存储姓名和电话
pair<string, int> p("Tom", 10086);
// 输出 pair 的第一个元素(姓名)和第二个元素(电话)
cout << "姓名:" << p.first << endl;
cout << "电话:" << p.second << endl;
// 使用 make_pair 创建 pair 对象,这是一种常见的方式
pair<string, int> p2 = make_pair("make", 191919190);
cout << "姓名:" << p2.first << endl;
cout << "电话:" << p2.second << endl;
// C++11 新特性,使用列表初始化 pair 对象
pair<string, int> p3 = { "hhhh",19298318 };
cout << "姓名:" << p3.first << endl;
cout << "电话:" << p3.second << endl;
// 判断两个 pair 对象是否相等,!= 表示不相等
if (p != p2)
{
cout << "不相等" << endl; // 如果 p 和 p2 不相等,输出提示
}
}
int main()
{
test0(); // 调用测试函数
return 0;
}
map:关联式容器
1. map 基本介绍
map 是 C++ 中的一种关联容器,通常用于存储键值对。每个元素都是 pair,其中第一个元素为 键(key),第二个元素为 值(value)。 map 容器的特点是:所有元素会根据 键 的大小自动排序,并且 键值唯一。如果需要允许重复键,可以使用 multimap。
2.map基本操作一览
map<键类型, 值类型, 比较器> mp
- 键类型:用于存储键的数据类型
- 值类型:用于存储与键相关联的值的数据类型
- 比较器:用于键值比较的规则,默认是 less<类型>,即从小到大排序,可以自定义为
greater<类型>
来改变排序顺序
2.1. map构造和赋值:对map容器进行构造和赋值操作
2.1.1 构造:
map<T1, T2> mp;
// map默认构造函数
map(const map& mp);
// 拷贝构造函数
2.1.2 赋值:
map& operator=(const map& mp);
// 重载等号操作符
2.2. map大小和交换:统计map容器的大小并交换两个map容器
size();
// 返回容器中的元素数量
empty();
// 判断容器是否为空
swap(st);
// 交换两个map容器
2.3. map插入和删除:对map容器插入和删除元素
insert(elem);
// 插入一个元素
clear();
// 清空所有元素
erase(pos);
// 删除pos迭代器所指的元素,返回下一个元素的迭代器
erase(beg, end);
// 删除区间[beg,end)的所有元素 ,返回下一个元素的迭代器。
erase(key);
// 删除键值为 key 的元素
2.4. map查找和统计:查找和统计数据
find(key);
// 查找key是否存在,存在则返回该键的元素的迭代器,不存在则返回map.end()
count(key);
// 统计键值为key的元素个数:
//map 中的键唯一,因此结果要么为 0(不存在),要么为 1(存在)。
2.5. map排序:map容器默认使用键值的升序排序
降序排序使用map<类型,类型,greater<类型>>m;
3. map 构造和赋值
3.1构造:
map 容器提供了几种方式来构造对象,包括默认构造、拷贝构造等。
- 默认构造: 使用默认构造函数创建一个空的 map 容器。
map<int, int> mp; // 创建一个空的 map 容器
- 拷贝构造: 通过拷贝另一个 map 容器来构造新的 map 对象。
map<int, int> mp2(mp); // 用 mp 初始化 mp2
3.2赋值:
通过重载等号操作符 =,可以将一个 map 容器的内容赋值给另一个 map。
map<int, int> mp3 = mp2; // 将 mp2 的内容赋值给 mp3
示例:
// 1. map构造和赋值
void test0()
{
// 默认构造 map 容器
map<int, int> m;
// 插入键值对
m.insert(pair<int, int>(1, 3));
m.insert(pair<int, int>(4, 0));
m.insert(pair<int, int>(1, 3)); // 重复键值不会插入
m.insert(pair<int, int>(4, 9)); // 键值相同,实值不同,仍然只保留键值唯一的元素
m.insert(pair<int, int>(5, 100));
print(m);
// 拷贝构造
map<int, int> m2(m); // 用 m 初始化 m2
print(m2);
// 赋值操作
map<int, int> m3 = m2; // 将 m2 的内容赋给 m3
print(m3);
}
4.map的遍历
// 默认排序遍历:
void print(map<int, int>& m)
{
// 使用迭代器遍历 map,按升序输出键值对
for (map<int, int>::iterator it = m.begin(); it != m.end(); it++)
{
cout << "键值:" << (*it).first << " " << "实值:" << it->second << " ";
cout << endl;
}
cout << endl;
// auto 简化迭代器类型(C++11)
// for (auto it = m.begin(); it != m.end(); it++)
// {
// cout << "键值:" << (*it).first << " " << "实值:" << it->second << " ";
// cout << endl;
// }
// 基于范围的 for 循环遍历 map (C++11)
// for (pair<int,int> mm : m)
// {
// cout << "键值:" << mm.first << " " << "实值:" << mm.second << " ";
// cout << endl;
// }
// 使用 auto 更简洁
// for (auto mm:m)
// {
// cout << "键值:" << mm.first << " " << "实值:" << mm.second << " ";
// cout << endl;
// }
}
// 降序遍历:使用 greater<int> 进行键值的降序排列
void print1(map<int, int, greater<int>>& m)
{
// 通过迭代器遍历 map,按降序输出键值对
for (map<int, int, greater<int>>::iterator it = m.begin(); it != m.end(); it++)
{
cout << "键值:" << it->first << " " << "实值:" << it->second << " ";
cout << endl;
}
cout << endl;
// 使用 auto 简化代码
// for (auto it = m.begin(); it != m.end(); it++)
// {
// cout << "键值:" << (*it).first << " " << "实值:" << it->second << " ";
// cout << endl;
// }
/*基于范围for循环:注意这里的元素类型为二元组
for (pair<int,int> mm : m)
{
cout << "键值:" << mm.first << " " << "实值:" << mm.second << " ";
cout << endl;
}*/
/*auto最好用,没有之一
for (auto mm:m)
{
cout << "键值:" << mm.first << " " << "实值:" << mm.second << " ";
cout << endl;
}*/
}
5. map 大小和交换
5.1获取容器大小:
.size():
返回 map 容器中的元素数量。
.empty():
判断 map 是否为空。
cout << "Size: " << m.size() << endl;
cout << "Is empty: " << m.empty() << endl;
5.2交换:
.swap():
交换两个 map 容器的内容。
map<int, int> m1, m2;
m1[1] = 10; m2[2] = 20;
m1.swap(m2); // 交换 m1 和 m2 的内容
示例:
// 2. map大小和交换
void test1()
{
map<int, int> m;
// 插入键值对
m.insert(pair<int, int>(1, 3));
m.insert(pair<int, int>(2, 0));
m.insert(pair<int, int>(5, 3));
// 判断 map 是否为空
if (m.empty())
{
cout << "m为空!" << endl;
}
else
{
cout << "m的长度为:" << m.size() << endl;
}
// 交换两个 map 容器
map<int, int> m1;
m1.insert(pair<int, int>(3, 1));
m1.insert(pair<int, int>(4, 1));
m1.insert(pair<int, int>(5, 3));
cout << "交换前:" << endl;
cout << "m: " << endl;
print(m);
cout << endl << "m1: " << endl;
print(m1);
// 执行交换
m.swap(m1);
cout << "交换后:" << endl;
cout << "m: " << endl;
print(m);
cout << endl << "m1: " << endl;
print(m1);
}
6. map 插入和删除
6.1 插入:
insert(elem)
:插入一个键值对元素。
分为:
.insert(pair<int, int>(first, second));
.insert(make_pair(first, second));
.insert(map<int, int>::value_type(first, second));
- 使用 [] 插入键值对。(好用)
m.insert(pair<int, int>(1, 100));
m[2] = 200;
6.2 删除:
erase(pos)
:删除pos迭代器所指的元素,返回下一个元素的迭代器
erase(beg, end)
:删除区间[beg,end)的所有元素 ,返回下一个元素的迭代器。
erase(key)
:删除键值为 key 的元素。
m.erase(2); // 删除键值为 2 的元素
m.erase(m.begin()); // 删除第一个元素
m.clear(); // 清空所有元素
示例:
// 3. map插入和删除
void test2()
{
map<int, int> m;
// 插入键值对的不同方法
m.insert(pair<int, int>(1, 1));
// 方法一:pair
m.insert(make_pair(2, 100));
// 方法二:make_pair
m.insert(map<int, int>::value_type(3, 44));
// 方法三:value_type
m[4] = 55;
// 方法四:中括号操作符
print(m);
// 删除元素
m.erase(m.begin()); // 删除迭代器指向的元素
print(m);
m.erase(3); // 删除键值为3的元素
print(m);
m.erase(m.begin(), m.end()); // 删除区间中的所有元素
//等价于m.clear();
print(m);
}
7. map 查找和统计
7.1查找:
find(key)
:查找 key,返回指向该元素的迭代器,如果不存在则返回 map.end()。
auto it = m.find(1);
if (it != m.end())
{
cout << "查找成功: " << it->first << "实值: " << it->second << endl;
}
else
{
cout << "查找失败" << endl;
}
7.2统计:
count(key)
:返回键值为 key 的元素个数,map 中的键唯一,因此结果要么为 0(不存在),要么为 1(存在)。
cout << "键值为1的元素数量: " << m.count(1) << endl;
示例:
// 4. map查找和统计
void test3()
{
map<int, int> m;
m[0] = 45;
m[4] = 34;
m[5] = 0;
// 查找键值为4的元素
map<int, int>::iterator it = m.find(4);
if (it != m.end())
{
cout << "查找成功:" << it->first << " " << it->second << endl;
}
else
{
cout << "查找失败" << endl;
}
// 统计键值为0的元素个数
if (m.count(0))
{
cout << "查找成功" << endl << "数量为:" << m.count(0) << endl;
}
else
{
cout << "查找失败" << endl;
}
}
8. map 排序
map 默认按照键的升序进行排序。如果需要自定义排序顺序,可以通过第三个模板参数指定比较器(如 greater)。
8.1默认升序排序:
map<int, int> m;
m[3] = 100;
m[1] = 200;
m[2] = 300;
8.2自定义降序排序:
map<int, int, greater<int>> m1;
m1[3] = 100;
m1[1] = 200;
m1[2] = 300;
示例:
// 5. map排序:键值默认从小到大排序
void test4()
{
// 默认升序排序
map<int, int> m;
m[0] = 11;
m[1] = 23;
m[-22] = 56;
print(m);
// 使用 greater<int> 进行降序排序
map<int, int, greater<int>> m1;
m1[0] = 11;
m1[1] = 23;
m1[-22] = 56;
print1(m1);
}
整体操作如下:
//map:关联式容器
// map中所有元素都是pair:
//匿名pair: pair<类型,类型>(键值,实值);
//pair中第一个元素为key(键值),起到索引作用,第二个元素为value(实值)
//所有元素都会根据元素的键值自动排序
//map不允许容器中有重复key值元素
//multimap允许容器中有重复key值元素
#include<iostream>
#include<map> // 引入 map 关联式容器头文件
using namespace std;
// map<键类型, 值类型, 比较器> mp
// 键类型:用于存储键的数据类型
// 值类型:用于存储与键相关联的值的数据类型
// 比较器:用于键值比较的规则,默认是 less<类型>,即从小到大排序,可以自定义为 greater<类型> 来改变排序顺序
// 1. map构造和赋值:对map容器进行构造和赋值操作
// 构造:
// map<T1, T2> mp; // map默认构造函数
// map(const map& mp); // 拷贝构造函数
// 赋值:
// map& operator=(const map& mp); // 重载等号操作符
// 2. map大小和交换:统计map容器的大小并交换两个map容器
// size(); // 返回容器中的元素数量
// empty(); // 判断容器是否为空
// swap(st); // 交换两个map容器
// 3. map插入和删除:对map容器插入和删除元素
// insert(elem); // 插入一个元素
// clear(); // 清空所有元素
// erase(pos); // 删除pos迭代器所指的元素,返回下一个元素的迭代器
// erase(beg, end); // 删除区间[beg,end)的所有元素 ,返回下一个元素的迭代器
// erase(key); // 删除键值为 key 的元素
// 4. map查找和统计:查找和统计数据
// find(key); // 查找key是否存在,存在则返回该键的元素的迭代器,不存在则返回map.end()
// count(key); // 统计键值为key的元素个数
// 5. map排序:map容器默认使用键值的升序排序
// 默认排序遍历:
void print(map<int, int>& m)
{
// 使用迭代器遍历 map,按升序输出键值对
for (map<int, int>::iterator it = m.begin(); it != m.end(); it++)
{
cout << "键值:" << (*it).first << " " << "实值:" << it->second << " ";
cout << endl;
}
cout << endl;
// auto 简化迭代器类型(C++11)
// for (auto it = m.begin(); it != m.end(); it++)
// {
// cout << "键值:" << (*it).first << " " << "实值:" << it->second << " ";
// cout << endl;
// }
// 基于范围的 for 循环遍历 map (C++11)
// for (pair<int,int> mm : m)
// {
// cout << "键值:" << mm.first << " " << "实值:" << mm.second << " ";
// cout << endl;
// }
// 使用 auto 更简洁
// for (auto mm:m)
// {
// cout << "键值:" << mm.first << " " << "实值:" << mm.second << " ";
// cout << endl;
// }
}
// 降序遍历:使用 greater<int> 进行键值的降序排列
void print1(map<int, int, greater<int>>& m)
{
// 通过迭代器遍历 map,按降序输出键值对
for (map<int, int, greater<int>>::iterator it = m.begin(); it != m.end(); it++)
{
cout << "键值:" << it->first << " " << "实值:" << it->second << " ";
cout << endl;
}
cout << endl;
// 使用 auto 简化代码
// for (auto it = m.begin(); it != m.end(); it++)
// {
// cout << "键值:" << (*it).first << " " << "实值:" << it->second << " ";
// cout << endl;
// }
/*基于范围for循环:注意这里的元素类型为二元组
for (pair<int,int> mm : m)
{
cout << "键值:" << mm.first << " " << "实值:" << mm.second << " ";
cout << endl;
}*/
/*auto最好用,没有之一
for (auto mm:m)
{
cout << "键值:" << mm.first << " " << "实值:" << mm.second << " ";
cout << endl;
}*/
}
// 1. map构造和赋值
void test0()
{
// 默认构造 map 容器
map<int, int> m;
// 插入键值对
m.insert(pair<int, int>(1, 3));
m.insert(pair<int, int>(4, 0));
m.insert(pair<int, int>(1, 3)); // 重复键值不会插入
m.insert(pair<int, int>(4, 9)); // 键值相同,实值不同,仍然只保留键值唯一的元素
m.insert(pair<int, int>(5, 100));
print(m);
// 拷贝构造
map<int, int> m2(m); // 用 m 初始化 m2
print(m2);
// 赋值操作
map<int, int> m3 = m2; // 将 m2 的内容赋给 m3
print(m3);
}
// 2. map大小和交换
void test1()
{
map<int, int> m;
// 插入键值对
m.insert(pair<int, int>(1, 3));
m.insert(pair<int, int>(2, 0));
m.insert(pair<int, int>(5, 3));
// 判断 map 是否为空
if (m.empty())
{
cout << "m为空!" << endl;
}
else
{
cout << "m的长度为:" << m.size() << endl;
}
// 交换两个 map 容器
map<int, int> m1;
m1.insert(pair<int, int>(3, 1));
m1.insert(pair<int, int>(4, 1));
m1.insert(pair<int, int>(5, 3));
cout << "交换前:" << endl;
cout << "m: " << endl;
print(m);
cout << endl << "m1: " << endl;
print(m1);
// 执行交换
m.swap(m1);
cout << "交换后:" << endl;
cout << "m: " << endl;
print(m);
cout << endl << "m1: " << endl;
print(m1);
}
// 3. map插入和删除
void test2()
{
map<int, int> m;
// 插入键值对的不同方法
m.insert(pair<int, int>(1, 1)); // 方法一:pair
m.insert(make_pair(2, 100)); // 方法二:make_pair
m.insert(map<int, int>::value_type(3, 44)); // 方法三:value_type
m[4] = 55; // 方法四:中括号操作符
print(m);
// 删除元素
m.erase(m.begin()); // 删除迭代器指向的元素
print(m);
m.erase(3); // 删除键值为3的元素
print(m);
m.erase(m.begin(), m.end()); // 删除区间中的所有元素
//等价于m.clear();
print(m);
}
// 4. map查找和统计
void test3()
{
map<int, int> m;
m[0] = 45;
m[4] = 34;
m[5] = 0;
// 查找键值为4的元素
map<int, int>::iterator it = m.find(4);
if (it != m.end())
{
cout << "查找成功:" << it->first << " " << it->second << endl;
}
else
{
cout << "查找失败" << endl;
}
// 统计键值为0的元素个数
if (m.count(0))
{
cout << "查找成功" << endl << "数量为:" << m.count(0) << endl;
}
else
{
cout << "查找失败" << endl;
}
}
// 5. map排序:键值默认从小到大排序
void test4()
{
// 默认升序排序
map<int, int> m;
m[0] = 11;
m[1] = 23;
m[-22] = 56;
print(m);
// 使用 greater<int> 进行降序排序
map<int, int, greater<int>> m1;
m1[0] = 11;
m1[1] = 23;
m1[-22] = 56;
print1(m1);
}
int main()
{
test0();
test1();
test2();
test3();
test4();
return 0;
}
9.相关注意事项
9.1. 键的唯一性
map 中的键必须是唯一的。这意味着你不能在 map 中插入具有相同键的多个元素。如果尝试插入一个已存在的键,map 将保持原来的值,不会更新或插入新的键值对。
map<int, int> m;
m.insert(make_pair(1, 100)); // 插入键值对 (1, 100)
m.insert(make_pair(1, 200)); // 插入失败,键 1 已存在
cout << "键值:" << m[1] << endl; // 输出 100
如果你希望插入时更新键值,可以使用 [] 操作符
m[1] = 200; // 更新键 1 的值为 200
9.2. 使用 [] 操作符时的注意
当你使用 map[key] 来访问键时,如果该键不存在,map 会默认插入该键,并将值初始化为零或默认值。这可能导致意外的元素插入。
map<int, int> m;
int value = m[5];
// 键 5 不存在,map 会自动插入键 5,其值为 0
cout << "m[5] = " << value << endl; // 输出 m[5] = 0
为避免这种行为,可以使用 find() 来检查键是否存在:
if (m.find(5) != m.end())
{
cout << "键 5 存在" << endl;
}
else
{
cout << "键 5 不存在" << endl;
}
9.3. 修改键
map 不允许修改键。一旦键被插入到 map 中,它的值可以被修改,但键不能被更改。如果需要修改键,必须先删除该键,然后再插入新的键值对。
map<int, int> m;
m.insert(make_pair(1, 100));
m.begin()->first = 2; // 错误,不能修改键
9.4. 避免重复插入(了解)
插入元素时,检查是否成功非常重要。map 的 insert() 函数返回一个 pair,其中 second 元素为 bool 值,指示插入是否成功。如果插入失败(因为键已存在),second 会是 false。
pair<map<int, int>::iterator, bool> ret = m.insert(make_pair(1, 100));
if (!ret.second)
{
cout << "插入失败,键 1 已存在" << endl;
}
9.5. 迭代器失效
在 map 中,插入或删除元素时,当前的迭代器不会失效。唯一会使迭代器失效的操作是删除迭代器指向的元素。 因此,当你删除某个元素时,确保适当地处理迭代器,以避免使用失效的迭代器。
for (auto it = m.begin(); it != m.end(); )
{
if (it->first == 3)
{
it = m.erase(it); // erase 返回下一个有效的迭代器
}
else
{
it++; // 手动递增迭代器
}
}
9.6总结
-
唯一键:map 不允许重复键,插入相同键时会保持原值。
-
[] 操作符自动插入:使用 [] 时要小心自动插入不存在的键。
-
不可修改键:map 中键值对的键不可修改,只能删除后重新插入。
-
避免迭代器失效:特别是在删除元素时,确保适当处理迭代器。