浅谈 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_updateAllocator:空间分配器类型
操作
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;
}

浙公网安备 33010602011771号