Appearance
第10章 pair 键值对详解
pair 是 STL 中最基础、最常用的工具模板,用来将两个值组合成一个整体,就像"一对"东西。它是 map 元素的基本组成单位,也是算法中存储"双关键字"数据的常用方式。
🎯 本章学习目标
学完本章,你将能够:
- 理解 pair 的作用(将两个值组合成一对)
- 熟练使用 pair 的定义与初始化方式
- 掌握 first 和 second 两个成员的访问
- 理解 pair 的字典序比较规则
- 掌握 pair 与 sort 的配合使用(按第一、第二关键字排序)
- 理解 pair 与 map/set 的关系(map 元素就是 pair)
- 熟练使用 make_pair 函数创建 pair
- 了解嵌套 pair 的用法
- 在编程中灵活运用 pair 解决多关键字排序等问题
🗺️ 本章知识图谱
pair 键值对
├── 核心概念
│ ├── 两个值的组合
│ ├── 模板类(可指定类型)
│ └── first / second
├── 基本用法
│ ├── 定义与初始化
│ ├── 访问(first、second)
│ ├── 修改
│ └── make_pair
├── 比较规则
│ ├── 字典序比较
│ ├── 先比 first,再比 second
│ └── 支持 == != < > <= >=
├── 与算法配合
│ ├── sort 排序(按 first 再 second)
│ └── 多关键字排序
├── 与容器配合
│ ├── map 的元素类型
│ ├── set 存 pair
│ ├── vector 存 pair
│ └── priority_queue 存 pair
├── 进阶用法
│ └── 嵌套 pair
└── 应用场景
├── 存坐标(x, y)
├── 存名次+名字
├── 存键值对(key-value)
└── 多关键字排序一、什么是 pair
🍎 生活比喻:pair 就像一双筷子——必须两根配成一对才能用。又像一张明信片——左边写地址(first),右边写姓名(second),两个信息合在一起才完整。
pair 是一个模板类,可以将两个不同类型(或相同类型)的值组合成一个整体。它只有两个成员变量:first 和 second,分别代表第一个值和第二个值。
pair 在 STL 中的地位非常重要:
- map 的元素就是 pair:map 中每个元素都是
pair<const key_type, value_type> - set 可以存 pair:用来存储双关键字的有序集合
- 算法中大量使用:排序、查找等算法常用 pair 来存储两个关联的值
二、pair 的定义与初始化
使用 pair 需要包含 <utility> 头文件(但很多时候通过包含 <map>、<vector> 等头文件已经间接包含了)。
1. 基本定义
cpp
#include <iostream>
#include <utility> // pair 的头文件
#include <string>
using namespace std;
int main() {
// 方式 1:默认构造(两个值都是默认值)
pair<int, string> p1; // first=0, second=""
// 方式 2:直接初始化
pair<int, string> p2(1, "apple"); // first=1, second="apple"
// 方式 3:用 make_pair 创建
pair<int, string> p3 = make_pair(2, "banana");
// 方式 4:C++11 列表初始化
pair<int, string> p4 = {3, "cherry"};
return 0;
}2. make_pair 函数
make_pair 是一个便捷函数,可以自动推导类型,创建 pair 更简洁:
cpp
auto p = make_pair(10, "hello");
// 等价于 pair<int, const char*> p(10, "hello");💡 小技巧:make_pair 的优势是不需要写模板参数,编译器自动推导类型。在 C++11 及以后,也可以用 {a, b} 的方式,但 make_pair 更通用、更清晰。
三、pair 的访问与修改
pair 的两个成员分别叫 first 和 second,通过 . 运算符访问:
cpp
#include <iostream>
#include <utility>
#include <string>
using namespace std;
int main() {
pair<int, string> p(101, "张三");
// 访问
cout << "学号:" << p.first << endl; // 输出:101
cout << "姓名:" << p.second << endl; // 输出:张三
// 修改
p.first = 102; // 修改 first
p.second = "李四"; // 修改 second
cout << "修改后 - 学号:" << p.first << ",姓名:" << p.second << endl;
return 0;
}⚠️ 易错点:pair 的成员是 first 和 second(都是小写),不是 first() 或 second(),它们是成员变量,不是成员函数。
四、pair 的比较(字典序)
pair 支持所有比较运算符:==、!=、<、>、<=、>=。比较规则是字典序:
- 先比较
first,如果不相等,结果就是first的比较结果; - 只有当
first相等时,才比较second。
🍎 生活比喻:就像查字典——先看第一个字(first),第一个字不同就不用看第二个了;第一个字相同,再看第二个字(second)。
cpp
#include <iostream>
#include <utility>
using namespace std;
int main() {
pair<int, int> p1(1, 5);
pair<int, int> p2(2, 3);
pair<int, int> p3(1, 8);
cout << boolalpha; // 输出 true/false 而不是 1/0
cout << "p1 < p2:" << (p1 < p2) << endl; // true(1 < 2,不用比 second)
cout << "p1 < p3:" << (p1 < p3) << endl; // true(first 都是 1,比 second:5 < 8)
cout << "p1 == p3:" << (p1 == p3) << endl; // false(first 相同,但 second 不同)
return 0;
}🔥 高频考点:pair 的字典序比较规则是选择题和编程题的高频考点,尤其是和 sort 配合的时候。
五、pair 与 sort 的配合
pair 最常用的场景之一就是配合 sort 进行多关键字排序。因为 pair 自带字典序比较,所以把数据存成 pair 再排序,可以轻松实现"按第一关键字排序,第一关键字相同再按第二关键字排序"。
示例:学生成绩排序
要求:先按分数从高到低排,分数相同按名字字典序排。
cpp
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <utility>
using namespace std;
int main() {
int n;
cin >> n;
vector<pair<int, string>> students; // pair<分数, 姓名>
for (int i = 0; i < n; i++) {
string name;
int score;
cin >> name >> score;
// 注意:分数取负数,这样升序排序就等于分数降序
students.push_back(make_pair(-score, name));
}
sort(students.begin(), students.end());
cout << "排名(分数从高到低,同分按姓名):" << endl;
for (int i = 0; i < students.size(); i++) {
cout << i + 1 << ". " << students[i].second
<< " " << -students[i].first << "分" << endl;
}
return 0;
}输入:
5
张三 90
李四 85
王五 90
赵六 95
钱七 85输出:
排名(分数从高到低,同分按姓名):
1. 赵六 95分
2. 王五 90分
3. 张三 90分
4. 李四 85分
5. 钱七 85分💡 小技巧:如果想让第一关键字降序、第二关键字升序,可以把第一关键字取负数(因为 sort 默认是升序)。这样负数的升序就等于原数的降序。
六、pair 与 map/set 的关系
1. map 的元素就是 pair
map 中存储的每个元素都是 pair<const key_type, value_type> 类型。迭代器指向的就是一个 pair:
cpp
map<int, string> mp;
mp[1] = "apple";
auto it = mp.begin();
cout << it->first << endl; // key:1
cout << it->second << endl; // value:"apple"所以,map 的 insert 操作就是插入一个 pair:
cpp
mp.insert(pair<int, string>(1, "apple"));
mp.insert(make_pair(2, "banana")); // 更常用2. set 也可以存 pair
set 存 pair 时,按 pair 的字典序排序(先比 first,再比 second):
cpp
set<pair<int, int>> s;
s.insert({3, 5});
s.insert({1, 2});
s.insert({3, 4});
// 排序结果:(1,2), (3,4), (3,5)七、嵌套 pair
pair 里还可以再套 pair,用来存储三个或更多的值:
cpp
// 三个值:用 pair 套 pair
pair<int, pair<int, string>> p;
p.first = 1;
p.second.first = 2;
p.second.second = "hello";🍎 生活比喻:嵌套 pair 就像套娃——大娃娃里面还有一个小娃娃,小娃娃里还有东西。不过套太多层会很难读,一般最多套两层(三个值),更多的值建议用结构体。
⚠️ 易错点:嵌套 pair 层级多了之后,first 和 second 很容易搞混。代码可读性会变差,复杂数据建议用自定义结构体。
八、常见应用场景
1. 存储坐标
cpp
pair<int, int> point(3, 5); // x=3, y=52. 存储名次 + 名字 / 分数 + 姓名
cpp
pair<int, string> rank_name(1, "冠军");3. 存储键值对(map 的底层)
cpp
pair<string, int> key_value("apple", 10); // key="apple", value=104. 多关键字排序
配合 sort 实现双关键字、三关键字排序。
5. 函数返回多个值
函数需要返回两个值时,可以返回一个 pair(虽然 C++11 以后也可以用 tuple,但 pair 更简单常用)。
九、常见误区与注意事项
⚠️ 易错点 1:first 和 second 拼错。这两个单词都是小写,是成员变量不是函数,后面不加括号。
⚠️ 易错点 2:sort pair 的排序方向。sort 默认是升序(从小到大),如果想让第一关键字降序,要么取负数,要么自定义比较函数。
⚠️ 易错点 3:pair 可以存不同类型。pair 的两个成员类型可以不一样,比如 pair<int, string>,不一定非得两个 int。
⚠️ 易错点 4:make_pair 的类型推导。make_pair(1, 2.5) 推导出来是 pair<int, double>,如果需要特定类型,最好显式定义 pair。
⚠️ 易错点 5:嵌套层级太深。pair 套 pair 虽然可以,但超过两层就很难读了,不如定义结构体。
十、🔖 本章小结
pair 是 STL 中最简单但也最常用的工具之一,它把两个值组合成一对,支持字典序比较。它是 map 的基本元素单位,也是多关键字排序的利器。掌握 pair 的使用,是学习 STL 关联式容器的基础,也是编写简洁高效代码的必备技能。
十一、📝 实战练习
练习题 1(选择题)
题目: pair 的两个成员变量名是?
A. first 和 second B. first() 和 second() C. key 和 value D. left 和 right
🔑 点击查看答案与解析
答案: A
**解析:**pair 的两个成员是 first 和 second,都是成员变量(不是函数,所以不加括号)。key 和 value 是概念上的说法,不是变量名。
练习题 2(判断题)
题目: pair 的两个元素类型必须相同( )
🔑 点击查看答案与解析
答案: 错误
**解析:**pair 的两个元素类型可以不同,比如 pair<int, string> 第一个是 int,第二个是 string。当然也可以相同,比如 pair<int, int>。
练习题 3(选择题)
题目: 比较两个 pair 大小时,比较规则是?
A. 先比较 second,再比较 first B. 先比较 first,再比较 second C. 同时比较 first 和 second D. 只比较 first,second 不参与比较
🔑 点击查看答案与解析
答案: B
**解析:**pair 按字典序比较:先比较 first,如果 first 不相等,结果就确定了;只有 first 相等时,才比较 second。就像查字典先看第一个字,第一个字相同再看第二个字。
练习题 4(程序阅读题)
题目: 阅读以下代码,写出输出结果:
cpp
#include <iostream>
#include <utility>
using namespace std;
int main() {
pair<int, int> p1(2, 5);
pair<int, int> p2(2, 3);
pair<int, int> p3(1, 10);
cout << (p1 > p2) << " " << (p3 < p1) << endl;
return 0;
}🔑 点击查看答案与解析
答案: 1 1(即 true true)
解析:
- p1 = (2, 5),p2 = (2, 3)
- first 都是 2,相等,继续比 second
- 5 > 3,所以 p1 > p2 为 true(输出 1)
- p3 = (1, 10),p1 = (2, 5)
- first:1 < 2,所以 p3 < p1 为 true(输出 1)
- second 不需要比了
最终输出:1 1
练习题 5(选择题)
题目: 以下关于 make_pair 的说法,正确的是?
A. make_pair 只能创建 pair<int, int> 类型 B. make_pair 需要显式指定模板参数 C. make_pair 会自动推导两个参数的类型 D. make_pair 是 pair 的成员函数
🔑 点击查看答案与解析
答案: C
解析:
- A 错误:make_pair 可以创建任意类型的 pair
- B 错误:make_pair 不需要指定模板参数,编译器自动推导
- C 正确:这就是 make_pair 的优势——简洁,不用写类型
- D 错误:make_pair 是一个全局函数,不是 pair 的成员函数
练习题 6(判断题)
题目: map 中的每个元素都是 pair 类型( )
🔑 点击查看答案与解析
答案: 正确
**解析:**map 存储的元素类型是 pair<const key_type, value_type>,其中 first 是 key(const,不可修改),second 是 value。所以用迭代器访问 map 元素时,it->first 是 key,it->second 是 value。
练习题 7(编程题)
题目: 输入 n 个学生的姓名和分数,按分数从高到低排序,如果分数相同,按姓名的字典序从小到大排序,输出排序后的名单。
**输入格式:**第一行一个整数 n,接下来 n 行每行一个姓名和一个分数 **输出格式:**n 行,每行姓名和分数
样例输入:
4
Alice 90
Bob 85
Charlie 90
David 95样例输出:
David 95
Alice 90
Charlie 90
Bob 85🔑 点击查看答案与解析
参考代码:
cpp
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <utility>
using namespace std;
int main() {
int n;
cin >> n;
vector<pair<int, string>> v; // pair<负分数, 姓名>,用负数实现降序
for (int i = 0; i < n; i++) {
string name;
int score;
cin >> name >> score;
v.push_back(make_pair(-score, name)); // 分数取负数
}
sort(v.begin(), v.end()); // 默认升序,负数升序 = 原分数降序
for (int i = 0; i < v.size(); i++) {
cout << v[i].second << " " << -v[i].first << endl;
}
return 0;
}**解析:**这是 pair + sort 的经典应用题。核心思路:
- 把数据存成
pair<分数, 姓名>的形式 - 因为 sort 默认是升序,而我们要分数降序,所以把分数取负数
- 排序后,first(负分数)升序 = 原分数降序
- first 相同时,second(姓名)按字典序升序,正好符合要求
这是双关键字排序最简洁的写法之一。
练习题 8(编程题)
题目: 输入 n 个平面上的点坐标(x, y),按 x 从小到大排序,如果 x 相同则按 y 从小到大排序,输出排序后的坐标。
**输入格式:**第一行一个整数 n,接下来 n 行每行两个整数 x y **输出格式:**n 行,每行两个整数 x y
样例输入:
5
3 5
1 2
3 2
2 4
1 1样例输出:
1 1
1 2
2 4
3 2
3 5🔑 点击查看答案与解析
参考代码:
cpp
#include <iostream>
#include <vector>
#include <algorithm>
#include <utility>
using namespace std;
int main() {
int n;
cin >> n;
vector<pair<int, int>> points;
for (int i = 0; i < n; i++) {
int x, y;
cin >> x >> y;
points.push_back(make_pair(x, y));
}
sort(points.begin(), points.end()); // pair 默认字典序,正好符合要求
for (int i = 0; i < points.size(); i++) {
cout << points[i].first << " " << points[i].second << endl;
}
return 0;
}**解析:**这道题完美契合 pair 的字典序比较规则!
- 先按 x 排序(first)
- x 相同按 y 排序(second)
- 都是升序
正好就是 pair 默认的比较规则,所以直接 sort 就行,一行代码搞定。这就是 pair 的威力——配合 sort 可以轻松实现双关键字排序。
十二、📊 考点总结表
| 知识点 | 重要程度 | 出题形式 | 易错点 |
|---|---|---|---|
| first/second 成员名 | ⭐⭐⭐⭐⭐ | 选择题、填空题 | 拼错、加括号 |
| 字典序比较规则 | ⭐⭐⭐⭐⭐ | 选择题、编程题 | 搞反比较顺序 |
| make_pair 函数 | ⭐⭐⭐⭐ | 选择题、编程题 | 不知道有这个函数 |
| 与 sort 配合排序 | ⭐⭐⭐⭐⭐ | 编程题 | 不会处理降序需求 |
| map 元素是 pair | ⭐⭐⭐⭐ | 选择题、判断题 | 不知道这个关系 |
| 两成员类型可不同 | ⭐⭐⭐ | 选择题 | 以为必须同类型 |
| 嵌套 pair | ⭐⭐⭐ | 选择题、编程题 | 层级太深可读性差 |
| 常见应用场景 | ⭐⭐⭐⭐ | 选择题、编程题 | 想不到用 pair |
⭐ 必背速记卡
┌──────────────────────────────────────┐
│ pair 必背速记卡 │
├──────────────────────────────────────┤
│ 1. 两个值组合成一对,first + second │
│ 2. 是模板类,两个类型可以不同 │
│ 3. first 和 second 是成员变量,不加()│
│ 4. 比较按字典序:先 first 后 second │
│ 5. make_pair 自动推导类型,创建更方便 │
│ 6. sort + pair = 双关键字排序神器 │
│ 7. map 的元素就是 pair(key-value) │
│ 8. set 也可以存 pair,双关键字有序 │
│ 9. 可嵌套 pair 存更多值,但别套太深 │
│ 10. 常用:存坐标、名次+名字、键值对 │
└──────────────────────────────────────┘