std::pair

std::pair

C++ std::pair的用法-CSDN博客

pair - OI Wiki (oi-wiki.org)

pair的应用

一种常用的方法是使用宏定义 #define mp make_pair,将有些冗长的 make_pair 化简为 mp

在 C++11 以及之后的版本中,make_pair 可以配合 auto 使用,以避免显式声明数据类型。

cpp 复制代码
auto 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");

访问两个元素(通过 firstsecond ):

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对象值的方法

  1. 使用"."操作符访问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
  1. 使用std::get函数访问pair对象的值。std::get函数接受一个整数模板参数,用于指定要访问的值的位置(从0开始)。

    C++标准库中,get<> 函数是用来访问元组(tuple)中的元素的,而不是用来直接访问 pair

    这段代码能够使用 std::get<>() 来访问 std::pair 的原因是从 C++11 开始,标准库为 std::pairstd::tuple 提供了模板化的 get 函数重载。这意味着你可以使用 std::get<0>(myPair) 来访问 pair 的第一个元素,使用 std::get<1>(myPair) 来访问第二个元素。这种方式是标准库对 pairtuple 的一致性支持的一部分,使得 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) 中的 01 是编译时常量,所以可以正常使用。为了方便,还是建议使用 .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 算法的堆优化中,可以使用 pairpriority_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();
  ...
}

这部分的工作流程是这样的:

  1. 检查队列不为空。
  2. 从队列中取出当前距离最短的节点信息(即pair中的first为距离,second为节点编号)。
  3. 从队列中移除这个元素。
  4. 使用这个节点的信息进行后续操作,比如更新这个节点相邻节点的距离。

通过这种方式,Dijkstra算法能高效地不断选出当前已知最短距离的节点,进而更新其他节点的最短距离,直到找到目的地的最短路径。希望这次的解释能帮助你理解这段代码的作用!

pair 与 map

map 的是 C++ 中存储键值对的数据结构。很多情况下,map 中存储的键值对通过 pair 向外暴露。

cpp 复制代码
map<int, double> m;
m.insert(make_pair(1, 2.0));

关于 map 更多的内容,请见 关联式容器无序关联式容器 中相关部分。

相关推荐
pianmian12 小时前
python数据结构基础(7)
数据结构·算法
好奇龙猫4 小时前
【学习AI-相关路程-mnist手写数字分类-win-硬件:windows-自我学习AI-实验步骤-全连接神经网络(BPnetwork)-操作流程(3) 】
人工智能·算法
sp_fyf_20245 小时前
计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-11-01
人工智能·深度学习·神经网络·算法·机器学习·语言模型·数据挖掘
香菜大丸5 小时前
链表的归并排序
数据结构·算法·链表
jrrz08285 小时前
LeetCode 热题100(七)【链表】(1)
数据结构·c++·算法·leetcode·链表
oliveira-time5 小时前
golang学习2
算法
南宫生6 小时前
贪心算法习题其四【力扣】【算法学习day.21】
学习·算法·leetcode·链表·贪心算法
懒惰才能让科技进步7 小时前
从零学习大模型(十二)-----基于梯度的重要性剪枝(Gradient-based Pruning)
人工智能·深度学习·学习·算法·chatgpt·transformer·剪枝
Ni-Guvara7 小时前
函数对象笔记
c++·算法
泉崎7 小时前
11.7比赛总结
数据结构·算法