Skip to content

第10章 pair 键值对详解 ​

pair 是 STL 中最基础、最常用的工具模板,用来将两个值组合成一个整体,就像"一对"东西。它是 map 元素的基本组成单位,也是算法中存储"双关键字"数据的常用方式。


🎯 本章学习目标 ​

学完本章,你将能够:

  1. 理解 pair 的作用(将两个值组合成一对)
  2. 熟练使用 pair 的定义与初始化方式
  3. 掌握 first 和 second 两个成员的访问
  4. 理解 pair 的字典序比较规则
  5. 掌握 pair 与 sort 的配合使用(按第一、第二关键字排序)
  6. 理解 pair 与 map/set 的关系(map 元素就是 pair)
  7. 熟练使用 make_pair 函数创建 pair
  8. 了解嵌套 pair 的用法
  9. 在编程中灵活运用 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 支持所有比较运算符:==、!=、<、>、<=、>=。比较规则是字典序:

  1. 先比较 first,如果不相等,结果就是 first 的比较结果;
  2. 只有当 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=5

2. 存储名次 + 名字 / 分数 + 姓名 ​

cpp
pair<int, string> rank_name(1, "冠军");

3. 存储键值对(map 的底层) ​

cpp
pair<string, int> key_value("apple", 10);  // key="apple", value=10

4. 多关键字排序 ​

配合 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 的经典应用题。核心思路:

  1. 把数据存成 pair<分数, 姓名> 的形式
  2. 因为 sort 默认是升序,而我们要分数降序,所以把分数取负数
  3. 排序后,first(负分数)升序 = 原分数降序
  4. 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. 常用:存坐标、名次+名字、键值对  │
└──────────────────────────────────────┘

百炼成钢,融会贯通