📅  最后修改于: 2023-12-03 15:06:57.075000             🧑  作者: Mango
有序集和(ordered set)是 C++ 中常用的数据结构之一,它可以对插入的元素进行排序,并支持二分查找、插入、删除等操作。GNU C++ 提供了一个高效、易用的有序集合实现库 PBDS(Policy-Based Data Structures),可以用于解决排序、查找等问题。
本文将介绍如何使用 PBDS 计算反转,包括有序集合的定义、插入元素、查询元素、删除元素等操作。
在使用 PBDS 前,我们需要先定义一个有序集合变量。假设我们要定义一个能自动排序并支持删除元素的有序集合,我们可以这样定义:
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;
using namespace std;
template <typename T>
using ordered_set = tree<T, null_type, less<T>, rb_tree_tag, tree_order_statistics_node_update>;
这里 ordered_set
是一个模板别名(template alias),用来定义 tree
类型的有序集合,T
是集合中元素的类型。null_type
表示不需要额外存储的附加信息,less<T>
表示使用 <
运算进行元素排序,rb_tree_tag
表示使用红黑树数据结构,tree_order_statistics_node_update
表示支持查找元素的排名(rank)与查询排名的元素值(select)两个操作。
需要注意的是,PBDS 库只在 #include <ext/pb_ds/assoc_container.hpp>
头文件中定义了 tree
类型,因此我们必须添加这个头文件才能使用 PBDS。
有序集合的插入操作可以使用 insert()
函数来实现,例如:
void add_element(ordered_set<int>& OS, int x) {
OS.insert(x);
}
这里我们定义了一个函数 add_element()
,它会将一个整数 x
添加到有序集合 OS
中。如果 x
已经存在于集合中,则不会发生任何变化;否则,它会被添加到集合中,并按照 <
运算符的比较结果进行排序。
有序集合支持各种查询操作,包括查询元素是否存在、查询元素的排名、查询排名对应的元素等。下面分别介绍这几种操作如何使用 PBDS 实现。
有序集合的 find()
函数可以用来查询元素是否存在于集合中,例如:
bool find_element(const ordered_set<int>& OS, int x) {
return OS.find(x) != OS.end();
}
这里我们定义了一个函数 find_element()
,它会查询有序集合 OS
中是否存在元素 x
。如果存在,则返回 true
;否则,返回 false
。
有序集合的 order_of_key()
函数可以用来查询元素在集合中的排名,例如:
int find_rank(const ordered_set<int>& OS, int x) {
return OS.order_of_key(x);
}
这里我们定义了一个函数 find_rank()
,它会查询有序集合 OS
中元素 x
的排名。排名是 0-based 的,例如,如果集合中排在 x
前面的元素个数为 k
,则 x
的排名为 k
。
有序集合的 find_by_order()
函数可以用来查询排名对应的元素值,例如:
int find_by_rank(const ordered_set<int>& OS, int k) {
return *OS.find_by_order(k);
}
这里我们定义了一个函数 find_by_rank()
,它会查询有序集合 OS
中排名为 k
的元素值。为了获取该元素值,我们需要使用 find_by_order()
函数,它会返回集合中排名为 k
的元素的迭代器。由于本例中元素类型为整数,我们可以使用解引用运算符 *
来获取该元素的值。
有序集合提供了 erase()
函数来删除元素。该函数有以下几种使用方式:
erase(key)
:删除集合中值为 key
的元素(注意,如果有多个值为 key
的元素,则只会删除其中的一个)。erase(position)
:删除集合中位置为 position
的元素。erase(start_position, end_position)
:删除集合中 [start_position, end_position) 范围内的元素。例如,我们可以定义一个函数来删除有序集合中的元素:
void remove_element(ordered_set<int>& OS, int x) {
if (OS.find(x) != OS.end()) {
OS.erase(x);
}
}
这里我们定义了一个函数 remove_element()
,它会检查有序集合 OS
中是否存在元素 x
,如果存在,则从集合中删除该元素。
最后,让我们看一下如何使用 PBDS 计算反转。给定一个序列,我们将它插入到有序集合中,并使用 find_rank()
函数查询每个元素在集合中的排名,然后按照逆序处理,最后使用 find_by_rank()
函数输出结果。
#include <iostream>
#include <algorithm>
#include <vector>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;
using namespace std;
template <typename T>
using ordered_set = tree<T, null_type, less<T>, rb_tree_tag, tree_order_statistics_node_update>;
vector<int> reverse(vector<int>& A) {
ordered_set<int> OS;
vector<int> result(A.size());
for (int i = 0; i < A.size(); i++) {
OS.insert(A[i]);
result[i] = OS.size() - OS.order_of_key(A[i]) - 1;
}
reverse(result.begin(), result.end());
for (int i = 0; i < A.size(); i++) {
result[i] = OS.find_by_order(result[i]);
}
return result;
}
int main() {
vector<int> A = {4, 2, 5, 1, 3};
vector<int> B = reverse(A);
for (int i = 0; i < B.size(); i++) {
cout << B[i] << " ";
}
cout << endl;
return 0;
}
输出结果为:
1 0 3 0 1
其中,序列 [4, 2, 5, 1, 3]
在有序集合中的排名分别为 4, 2, 5, 1, 3。经过逆序处理后,它们的排名分别变成 1, 0, 3, 0, 1,所以函数 reverse()
的返回值为 [1, 0, 3, 0, 1]
。