浅谈 pb_ds 库
大部分是在wiki搬运的,只是方便我看
简介
pb_ds 库封装了很多数据结构,比如哈希(Hash)表,平衡二叉树,字典树(Trie 树),堆(优先队列)等。
就像 vector
、set
、map
一样,其组件均符合 STL 的相关接口规范。部分(如优先队列)包含 STL 内对应组件的所有功能,但比 STL 功能更多。
可以使用 begin()
和 end()
来获取 iterator
从而遍历
可以 increase_key
,decrease_key
以及删除单个元素
pbds 中的堆
申请
#include <ext/pb_ds/priority_queue.hpp>
using namespace __gnu_pbds;
__gnu_pbds::priority_queue<T,Compare,Tag,Allocator> t;
模板形参
在__gnu_pbds::priority_queue<T,Compare,Tag,Allocator> t
中:
-
T
: 储存的元素类型 -
Compare
: 提供严格的弱序比较类型 -
Tag
: 是__gnu_pbds
提供的不同的五种堆,Tag 参数默认是pairing_heap_tag
五种分别是:pairing_heap_tag
:配对堆 官方文档认为在非原生元素(如自定义结构体/std :: string
/pair
)中,配对堆表现最好binary_heap_tag
:二叉堆 官方文档认为在原生元素中二叉堆表现最好,不过笔者测试的表现并没有那么好binomial_heap_tag
:二项堆 二项堆在合并操作的表现要优于二叉堆,但是其取堆顶元素操作的复杂度比二叉堆高rc_binomial_heap_tag
:冗余计数二项堆thin_heap_tag
:除了合并的复杂度都和 Fibonacci 堆一样的一个 tag
-
Allocator
:空间配置器。
操作
push()
: 向堆中压入一个元素,返回该元素位置的迭代器。pop()
: 将堆顶元素弹出。top()
: 返回堆顶元素。size()
返回元素个数。empty()
返回是否非空。modify(point_iterator, const key)
: 把迭代器位置的key
修改为传入的key
,并对底层储存结构进行排序。erase(point_iterator)
: 把迭代器位置的键值从堆中擦除。
实例
#include <algorithm>
#include <cstdio>
#include <ext/pb_ds/priority_queue.hpp>
#include <iostream>
using namespace __gnu_pbds;
// 由于面向OIer, 本文以常用堆 : pairing_heap_tag作为范例
// 为了更好的阅读体验,定义宏如下 :
#define pair_heap __gnu_pbds ::priority_queue<int>
pair_heap q1; // 大根堆, 配对堆
pair_heap q2;
pair_heap ::point_iterator id; // 一个迭代器
int main() {
id = q1.push(1);
// 堆中元素 : [1];
for (int i = 2; i <= 5; i++) q1.push(i);
// 堆中元素 : [1, 2, 3, 4, 5];
std ::cout << q1.top() << std ::endl;
// 输出结果 : 5;
q1.pop();
// 堆中元素 : [1, 2, 3, 4];
id = q1.push(10);
// 堆中元素 : [1, 2, 3, 4, 10];
q1.modify(id, 1);
// 堆中元素 : [1, 1, 2, 3, 4];
std ::cout << q1.top() << std ::endl;
// 输出结果 : 4;
q1.pop();
// 堆中元素 : [1, 1, 2, 3];
id = q1.push(7);
// 堆中元素 : [1, 1, 2, 3, 7];
q1.erase(id);
// 堆中元素 : [1, 1, 2, 3];
q2.push(1), q2.push(3), q2.push(5);
// q1中元素 : [1, 1, 2, 3], q2中元素 : [1, 3, 5];
q2.join(q1);
// q1中无元素,q2中元素 :[1, 1, 1, 2, 3, 3, 5];
}
感觉大部分都从wiki上搬运的,但是我还是觉得可能和STL里的priority_queue差不多?
pbds 中的平衡树
申请
#include <ext/pb_ds/assoc_container.hpp> // 因为tree定义在这里 所以需要包含这个头文件
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;
__gnu_pbds ::tree<Key, Mapped, Cmp_Fn = std::less<Key>, Tag = rb_tree_tag,
Node_Update = null_tree_node_update,
Allocator = std::allocator<char> >
tr ;
模板形参
Key
: 储存的元素类型,如果想要存储多个相同的Key
元素,则需要使用类似于std::pair
和struct
的方法,并配合使用lower_bound
和upper_bound
成员函数进行查找Mapped
: 映射规则(Mapped-Policy)类型,如果要指示关联容器是 集合,类似于存储元素在std::set
中,此处填入null_type
,低版本g++
此处为null_mapped_type
;如果要指示关联容器是 带值的集合,类似于存储元素在std::map
中,此处填入类似于std::map<Key, Value>
的Value
类型Cmp_Fn
: 关键字比较函子,例如std::less<Key>
Tag
: 选择使用何种底层数据结构类型,默认是rb_tree_tag
。
__gnu_pbds
提供不同的三种平衡树,分别是:rb_tree_tag
:红黑树,一般使用这个,后两者的性能一般不如红黑树splay_tree_tag
:splay 树
Node_Update
:用于更新节点的策略,默认使用null_node_update
,若要使用order_of_key
和find_by_order
方法,需要使用tree_order_statistics_node_update
Allocator
:空间分配器类型
操作
insert(x)
:向树中插入一个元素 x,返回std::pair<point_iterator, bool>
。erase(x)
:从树中删除一个元素/迭代器 x,返回一个bool
表明是否删除成功。order_of_key(x)
:返回 x 以Cmp_Fn
比较的排名。find_by_order(x)
:返回Cmp_Fn
比较的排名所对应元素的迭代器。lower_bound(x)
:以Cmp_Fn
比较做lower_bound
,返回迭代器。upper_bound(x)
:以Cmp_Fn
比较做upper_bound
,返回迭代器。join(x)
:将 x 树并入当前树,前提是两棵树的类型一样,x 树被删除。split(x,b)
:以Cmp_Fn
比较,小于等于 x 的属于当前树,其余的属于 b 树。empty()
:返回是否为空。size()
:返回大小。
实例
// Common Header Simple over C++11
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
typedef pair<int, int> pii;
#define pb push_back
#define mp make_pair
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
__gnu_pbds ::tree<pair<int, int>, __gnu_pbds::null_type, less<pair<int, int> >,
__gnu_pbds::rb_tree_tag,
__gnu_pbds::tree_order_statistics_node_update>
trr;
int main() {
int cnt = 0;
trr.insert(mp(1, cnt++));
trr.insert(mp(5, cnt++));
trr.insert(mp(4, cnt++));
trr.insert(mp(3, cnt++));
trr.insert(mp(2, cnt++));
// 树上元素 {{1,0},{2,4},{3,3},{4,2},{5,1}}
auto it = trr.lower_bound(mp(2, 0));
trr.erase(it);
// 树上元素 {{1,0},{3,3},{4,2},{5,1}}
auto it2 = trr.find_by_order(1);
cout << (*it2).first << endl;
// 输出排名 0 1 2 3 中的排名 1 的元素的 first:1
int pos = trr.order_of_key(*it2);
cout << pos << endl;
// 输出排名
decltype(trr) newtr;
trr.split(*it2, newtr);
for (auto i = newtr.begin(); i != newtr.end(); ++i) {
cout << (*i).first << ' ';
}
cout << endl;
// {4,2},{5,1} 被放入新树
trr.join(newtr);
for (auto i = trr.begin(); i != trr.end(); ++i) {
cout << (*i).first << ' ';
}
cout << endl;
cout << newtr.size() << endl;
// 将 newtr 树并入 trr 树,newtr 树被删除。
return 0;
}