std::pair
pair的应用
一种常用的方法是使用宏定义
#define mp make_pair
,将有些冗长的make_pair
化简为mp
。在 C++11 以及之后的版本中,
make_pair
可以配合auto
使用,以避免显式声明数据类型。
cppauto p3 = make_pair(1, 2.0);
关于
auto
的在信息学竞赛中的使用,参见 迭代器 部分的说明。
pair 是 C++ 标准库的一个模板类,用于存储两个不同类型的值。它可以将两个值组合成一个单元,方便在函数返回多个值或者在容器中存储多个类型的数据。我们可以通过 pair 来方便地存储和操作两个不同类型的值。
定义(构造):
cpp
//使用默认构造函数
pair<int, double> p1;
//用给定值初始化
pair<int, double> p2(1, 2.4);
//拷贝构造函数
pair<int, double> p3(p2);
由于 pair 类型的使用比较繁琐,因为如果要定义多个形同的 pair 类型的时候,可以时候 typedef 简化声明:
cpp
typedef pair<string, string> author;
author pro("May", "Lily");
author joye("James", "Joyce");
访问两个元素(通过 first
和 second
):
cpp
pair<int, double> p1; //使用默认构造函数
p1.first = 1;
p1.second = 2.5;
cout << p1.first << ' ' << p1.second << endl;
赋值operator = :
(1)使用了 make_pair 函数来创建一个pair对象。
make_pair 函数接受两个参数,分别是要组合的两个值,然后返回一个pair对象。
cpp
pair<int, double> p1; // 声明一个pair对象,其中第一个值的类型是int,第二个值的类型是double
p1 = make_pair(1, 1.2); // 使用make_pair函数将1和1.2组合成一个pair对象,并将其赋值给p1
在这个例子中,p1是一个pair对象,它的第一个值是1,第二个值是1.2。
(1.1)可以使用 make_pair 对已存在的两个数据构造一个新的 pair 类型:
cpp
int a = 8;
string m = "James";
pair<int, string> newone;
newone = make_pair(a, m);
(2)变量间赋值:
可以将 pair
的值赋给另一个类型一致的 pair
。
cpp
pair<int, double> p1(1, 1.2);
pair<int, double> p2 = p1;
访问pair对象值的方法
- 使用"."操作符访问pair对象的第一个和第二个值。
cpp
#include <iostream>
#include <utility>
int main() {
std::pair<int, double> myPair(10, 3.14);
int firstValue = myPair.first;
double secondValue = myPair.second;
std::cout << "First value: " << firstValue << std::endl;
std::cout << "Second value: " << secondValue << std::endl;
return 0;
}
输出:
sql
First value: 10
Second value: 3.14
也可以对其进行修改。
cpp
int firstValue = p1.first++; // firstValue 为 10
int firstValue = ++p1.first; // firstValue 为 11
-
使用std::get函数访问pair对象的值。std::get函数接受一个整数模板参数,用于指定要访问的值的位置(从0开始)。
C++标准库中,
get<>
函数是用来访问元组(tuple
)中的元素的,而不是用来直接访问pair
。这段代码能够使用
std::get<>()
来访问std::pair
的原因是从 C++11 开始,标准库为std::pair
和std::tuple
提供了模板化的get
函数重载。这意味着你可以使用std::get<0>(myPair)
来访问pair
的第一个元素,使用std::get<1>(myPair)
来访问第二个元素。这种方式是标准库对pair
和tuple
的一致性支持的一部分,使得pair
可以被视为两元素的特殊tuple
,因此你可以像操作tuple
那样来操作pair
。
cpp
#include <iostream>
#include <utility>
#include <tuple>
int main() {
std::pair<int, double> myPair(10, 3.14);
int firstValue = std::get<0>(myPair);
double secondValue = std::get<1>(myPair);
std::cout << "First value: " << firstValue << std::endl;
std::cout << "Second value: " << secondValue << std::endl;
return 0;
}
注意:模板参数需要在编译时确定,不能使用变量。
std::get<0>(myPair)
和std::get<1>(myPair)
中的0
和1
是编译时常量,所以可以正常使用。为了方便,还是建议使用.first
还是.second
来访问pair
输出:
sql
First value: 10
Second value: 3.14
也可以对其进行修改。
cpp
double secondValue = get<1>(myPair)++; // secondValue 为 3.14
double secondValue = ++get<1>(myPair); // secondValue 为 4.14
应用举例
离散化
pair
可以轻松实现离散化。
我们可以创建一个 pair
数组,将原始数据的值作为每个 pair
第一个变量,将原始数据的位置作为第二个变量。在排序后,将原始数据值的排名(该值排序后所在的位置)赋给该值原本所在的位置即可。
cpp
// a为原始数据
pair<int, int> a[MAXN];
// ai为离散化后的数据
int ai[MAXN];
for (int i = 0; i < n; i++) {
// first为原始数据的值,second为原始数据的位置
scanf("%d", &a[i].first);
a[i].second = i;
}
// 排序
sort(a, a + n);
for (int i = 0; i < n; i++) {
// 将该值的排名赋给该值原本所在的位置
ai[a[i].second] = i;
}
好的,我来用更简单的方式解释一下。
离散化这个概念可能听起来有点复杂,但其实就是一个将大杂烩的数据变得井井有条的过程。想象一下,你有一堆乱七八糟的数,这些数有的很大,有的很小,你想要把它们整理得更有序一些,而且希望处理之后的数不会太大,便于管理。
在C++中,pair
这个工具可以帮你做到这一点。你可以把每一个数(我们称之为"值")和它在原始列表中的位置(可以想象成这个数的"家庭住址")绑定到一起,这样每个数就不会迷路了。这种绑定就是通过创建一个包含两个部分的pair
来实现的,第一个部分是数值本身,第二个部分是这个数值的位置。
接下来的步骤是把这些绑定了数值和位置的pair
进行排序。排序是根据数值的大小进行的,也就是说,我们会根据数值把这些pair
从小到大排列好。排序后,每个数值的"新位置"就体现出来了。这个"新位置"实际上就是它在排序后的序列中的位置,可以理解为这个数值在所有数值中的排名。
最后一步,就是把这个"排名"赋给原来的位置。因为每个pair
都记着原来的位置,所以我们可以通过这个位置,把"排名"放回到一个新数组中相应的位置。这样一来,原始的数据就被转换成了一个包含排名的数组,完成了离散化。
简单来说,就是通过pair
这个工具,把原始的一堆数,按照大小给它们排序,然后根据这个排序的结果,把每个数的排名告诉它原来的位置,这样就把乱七八糟的数据变得有条不紊了。希望这次的解释能让你更加明白!
我的理解
假设 <math xmlns="http://www.w3.org/1998/Math/MathML"> n = 7 n = 7 </math>n=7
pair<int, int> a[MAXN]
的 每个元素如下
scss
(6,0) (3,1) (2,2) (5,3) (6,4) (100,5) (1,6)
sort(a, a+n)
之后
scss
(1,6) (2,2) (3,1) (5,3) (6,0) (6,4) (100,5)
// 将该值的排名赋给该值原本所在的位置 ai[a[i].second] = i;
ini
ai[6] = 0;
ai[2] = 1;
ai[1] = 2;
ai[3] = 3;
ai[0] = 4;
ai[4] = 5;
ai[5] = 6;
Dijkstra【不太明白,学成归来时,日后再看】
如前所述,pair
可以作为 priority_queue
的数据类型。
那么,在 Dijkstra 算法的堆优化中,可以使用 pair
与 priority_queue
维护节点,将节点当前到起点的距离作为第一个变量,将节点编号作为第二个变量。
cpp
priority_queue<pair<int, int>, std::vector<pair<int, int> >,
std::greater<pair<int, int> > >
q;
... while (!q.empty()) {
// dis为入堆时节点到起点的距离,i为节点编号
int dis = q.top().first, i = q.top().second;
q.pop();
...
}
在Dijkstra算法中,使用pair
结合priority_queue
(优先队列)是一种常见的优化手段,它可以有效地管理和更新节点的访问顺序,尤其是在寻找最短路径问题中。让我来解释一下这段代码是如何工作的:
Dijkstra算法的目的是在加权图中找到从起点到其他所有点的最短路径。算法的关键之一是需要频繁地选出当前已知最短距离中的最小值,来更新其他点的距离。使用priority_queue
正是为了优化这个选择过程。
priority_queue
是一个容器适配器,它能保证每次取出的元素都是队列中优先级最高的。在这个场景中,优先级最高意味着到起点的距离最短。
pair<int, int>
在这里的作用是将节点的两个关键信息绑定在一起:节点到起点的当前已知最短距离(first
)和节点的编号(second
)。这样,当你需要更新节点距离时,也能知道这个距离是属于哪个节点的。
在priority_queue
的定义中,pair<int, int>
是队列中存储的元素类型,std::vector<pair<int, int>>
是底层容器类型,而std::greater<pair<int, int>>
是一个比较函数对象,用来确定队列的排序顺序。在这个例子中,std::greater<>
使得队列以最小的距离优先出队,这正符合Dijkstra算法的需求。
代码片段中的循环:
cpp
while (!q.empty()) {
int dis = q.top().first, i = q.top().second;
q.pop();
...
}
这部分的工作流程是这样的:
- 检查队列不为空。
- 从队列中取出当前距离最短的节点信息(即
pair
中的first
为距离,second
为节点编号)。 - 从队列中移除这个元素。
- 使用这个节点的信息进行后续操作,比如更新这个节点相邻节点的距离。
通过这种方式,Dijkstra算法能高效地不断选出当前已知最短距离的节点,进而更新其他节点的最短距离,直到找到目的地的最短路径。希望这次的解释能帮助你理解这段代码的作用!
pair 与 map
map
的是 C++ 中存储键值对的数据结构。很多情况下,map
中存储的键值对通过 pair
向外暴露。
cpp
map<int, double> m;
m.insert(make_pair(1, 2.0));