pb_ds(Policy-Based Data Structures)
来源:互联网 发布:gre模拟考试软件 编辑:程序博客网 时间:2024/05/11 03:23
参考:http://blog.csdn.net/wjf_wzzc/article/details/38851703
《C++的pb_ds库在OI中的应用》大连市第二十四中学 于纪平
http://blog.csdn.net/wmdcstdio/article/details/44596305
一、
pb_ds 库全称 Policy-Based Data Structures
It is designed for high-performance, flexibility, semantic safety, and conformance to the corresponding containers in std and std::tr1.
英文官方文档:https://gcc.gnu.org/onlinedocs/libstdc++/ext/pb_ds/
pb_ds 库封装了很多数据结构,比如哈希(Hash)表,平衡二叉树,字典树(Trie树),堆(优先队列)等
就像 vector、set、map 一样,但比STL 功能更多
pb_ds 只在 Linux 下可以用
二、基本用法
1、优先队列(Priority Queue)
#include<ext/pb_ds/priority_queue.hpp> using namespace __gnu_pbds;
与 std::priority queue 的基本用法相同,包括 size(),empty(),push(const T),top(),pop(),clear()
模版参数:
template
<
typename Value_Type ,
typename Cmp_Fn = std :: less < Value_Type >,
typename Tag = pairing_heap_tag ,
typename Allocator = std :: allocator <char >
>
class priority_queue
Tag 表示所使用的堆的类型
可以是配对堆(pairing_heap_tag)、二叉堆(binary_heap_tag)、二项堆(binomial_heap_tag)、经改良的斐波那契堆(thin_heap_tag)、
冗余计数二项式堆(rc_binomial_heap_tag)等
可以用 begin() 和 end() 来获取 iterator 从而遍历
可以 increase_key,decrease_key 和删除单个元素
可以合并
* point iterator push(const reference)
* void modify(point iterator, const reference)
* void erase(point iterator)
priority_queue <int > p;
priority_queue <int >:: point_iterator it = p. push (0);
p. push (1); p. push (2);
p. modify (it , 3);
assert (p. top () == 3);
p. erase (it );
assert (p. top () == 2);
* void join(priority queue &other)
把 other 合并到 *this,然后 other 会被清空
共有 5 种操作:push、pop、modify、erase、join
配对堆(pairing_heap_tag):push 和 join 为 O(1),其余为均摊 O(logn)
二叉堆(binary_heap_tag) :只支持 push 和 pop,均为均摊 O(logn)
二项堆(binomial_heap_tag):push 为均摊 O(1),其余为 O(logn)
冗余计数二项式堆(rc_binomial_heap_tag):push 为 O(1),其余为 O(logn)
经改良的斐波那契堆(thin_heap_tag):push 为 O(1),不支持 join,其余为 O(logn);但是如果只有 increase_key,那么 modify 为均摊 O(1)
“不支持”不是不能用,而是用起来很慢
通常需要合并的时候就用配对堆(pairing_heap_tag)
做 Dijkstra 的时候就用经改良的斐波那契堆(thin_heap_tag)
真的是这样吗?
实践是检验真理的唯一标准!
性能测试:
参与测试的有:
std::priority_queue,配对堆(pairing_heap_tag)、二叉堆(binary_heap_tag)、二项堆(binomial_heap_tag)、经改良的斐波那契堆(thin_heap_tag)、冗余计数二项式堆(rc_binomial_heap_tag),手写线段树
评测机:
Windows,i5-3210M 2.50GHz,gcc version 4.4.0 (GCC)
测试1:堆排序
测试2:堆的合并
测试3:堆优化 Dijkstra 算法
不开 O2
开 O2
大致结论
2、平衡二叉树(Balanced Binary Tree)
在刷题过程中我们经常使用到set和map,也知道它们的内部实现是红黑树,但我们显然无法按照操作平衡二叉树的方式操作它们,甚至连平衡二叉树最基本的求kth和求rank操作都无法在O(logn)内完成
好在pb_ds库给了一个并不是很强大,但在一些时候足够用的解决方案
pb_ds库内置了红黑树(Red Black Tree)、伸展树(Splay Tree)等
这些封装好的树都支持插入(insert)、删除(erase)、求区间第 k 大(find_by_order)、求 rank(order_of_key)操作
声明变量时的参数(模板参数):
第一个是键(key)的类型
第二个是值(value)的类型,null_type表示没有值,简单地理解就是表明这是 set 而不是 map,有些稍旧的版本需要写成 null_mapped_type才可以
第三个表示比较函数,默认为 less<Type>
第四个为平衡二叉树的类型,默认为红黑树rb_tree_tag
第五个代表元素的维护策略,只有当使用 tree_order_statistics_node_update 时才可以求 kth 和 rank,此外还有 null_tree_node_update(默认值)等
注意:
求 kth(find_by_order)返回的是迭代器,求 rank 返回的是值,两者都是从 0 开始计算的
BZOJ 3224 Tyvj 1728 普通平衡树
解题思路:
需要实现一个支持点插入,点删除,查询rank,求第k大,求前驱/后继的数据结构正好可以用pb_ds中的tree实现pb_d s中 tree 的功能:
set / map 支持的所有功能
还包括查询 rank,求第 k 大,按值分割(把值 >=x 的割到另一棵树中)
合并值域不相交的两棵树为了实现查询 rank 和求第 k 大,必须加上 tree_order_statistics_node_update
注意:
①null_mapped_type 是用来占位的,如果它是一个真正的数据类型那就是 map,如果像这样就是 set
在一些编译器上(如 COGS 、BZOJ 和SPOJ 的编译器)必须用 null_mapped_type,在另一些编译器上必须用 null_type②即使键类型是 long long,你仍然可以强行把比较器定义成less或者greater,当然会出bug③pb_ds 不资瓷类似 multiset / multimap 的玩意,你必须自行搞一个偏移量
可以把每个元素乘以2^20后加上偏移量,偏移量是 "当前重复了几次",再用一个 map 来维护 “每个元素出现了几次”,前驱后继直接在 map 上计算
#include <iostream>#include <cstdio>#include <cstring>#include <cstdlib>#include <algorithm>#include <queue>#include <vector>#include <stack>#include <map>#include <cmath>#include <cctype>#include <ext/pb_ds/assoc_container.hpp>using namespace std;using namespace __gnu_pbds;typedef long long ll;typedef unsigned long long ull;typedef unsigned int uint;typedef tree<ll, null_mapped_type, less<ll>, rb_tree_tag, tree_order_statistics_node_update> SuperTree;const ull mod = 1e9 + 7;const int INF = 0x7fffffff;SuperTree Tree;map<int, int> Map;int main(){#ifdef __AiR_H freopen("in.txt", "r", stdin);#endif // __AiR_H int n; scanf("%d", &n); while (n--) { int opt; ll x; scanf("%d%lld", &opt, &x); if (opt == 1) { Tree.insert((x<<20) + Map[x]++); } else if (opt == 2) { Tree.erase(Tree.find((x<<20) + (--Map[x]))); if (Map[x] == 0) { Map.erase(Map.find(x)); } } else if (opt == 3) { printf("%d\n", Tree.order_of_key(x<<20) + 1); } else if (opt == 4) { printf("%lld\n", *Tree.find_by_order(x-1)>>20); } else if (opt == 5) { map<int, int>::iterator itr = Map.lower_bound(x); --itr; printf("%d\n", itr->first); } else { map<int, int>::iterator itr = Map.upper_bound(x); printf("%d\n", itr->first); } } return 0;}
UESTC 1339 郭大侠与线上游戏
可以查询区间第 k 大,然后 k 取中位数
#include <iostream>#include <cstdio>#include <cstring>#include <cstdlib>#include <algorithm>#include <queue>#include <vector>#include <stack>#include <map>#include <cmath>#include <cctype>#include <ext/pb_ds/assoc_container.hpp>using namespace std;using namespace __gnu_pbds;typedef tree<int, null_type, less<int>, rb_tree_tag, tree_order_statistics_node_update> set_t;typedef long long ll;typedef unsigned long long ull;typedef unsigned int uint;const ull mod = 1e9 + 7;const int INF = 0x7fffffff;const int maxn = 1e6 + 10;queue<int> Q;set_t s;int main(){#ifdef __AiR_H freopen("in.txt", "r", stdin);// freopen("out.txt", "w", stdout);#endif // __AiR_H int n; int cmd, x; scanf("%d", &n); while (n--) { scanf("%d", &cmd); if (cmd == 1) { scanf("%d", &x); s.insert(x); Q.push(x); } else if (cmd == 2) { s.erase(Q.front()); Q.pop(); } else { int k = Q.size() / 2; printf("%d\n", *s.find_by_order(k)); } } return 0;}
BZOJ 1503 [NOI2004]郁闷的出纳员
解题思路:
要求实现:插入,求第k大,删除小于某个值的元素(需要记录删了多少)
“删除小于某个值的元素”用 split (按值分割)实现,用 greater 比较,删除的命令类似于 Tree.split(low,Tree_t),其中 Tree_t 是一棵无用的树
#include <iostream>#include <cstdio>#include <cstring>#include <cstdlib>#include <algorithm>#include <queue>#include <vector>#include <stack>#include <map>#include <cmath>#include <cctype>#include <ext/pb_ds/assoc_container.hpp>using namespace std;using namespace __gnu_pbds;typedef long long ll;typedef unsigned long long ull;typedef unsigned int uint;typedef tree<ll, null_mapped_type, greater<ll>, rb_tree_tag, tree_order_statistics_node_update> SuperTree;const ull mod = 1e9 + 7;const int INF = 0x7fffffff;SuperTree Tree, Tree_t;char cmd[2];int main(){#ifdef __AiR_H freopen("in.txt", "r", stdin);#endif // __AiR_H int n, Min; scanf("%d%d", &n, &Min); int Count = 0, Leave = 0; int Add = 0; for (int i = 0; i < n; ++i) { ll k; scanf("%s%lld", cmd, &k); if (cmd[0] == 'I') { if (k >= Min) { k -= Add; ++Count; Tree.insert((k<<20) + i); } } else if (cmd[0] == 'A') { Add += k; } else if (cmd[0] == 'S') { Add -= k; ll low = Min - Add; int leave_t = Count - Tree.order_of_key(low<<20); Count -= leave_t, Leave += leave_t; Tree.split(low<<20, Tree_t); //按值分割 } else { if (k <= Count) { printf("%lld\n", (*Tree.find_by_order(k-1)>>20) + Add); } else { printf("-1\n"); } } } printf("%d\n", Leave); return 0;}
模板参数
更多用法
性能测试
测试一:平衡排序树
测试二:字典
hash_table
总结
- pb_ds(Policy-Based Data Structures)
- Policy-based data structures (GNC)系列译文总序
- 2016 UESTC Training for Data Structures I - 郭大侠与线上游戏 CDOJ 1339 pb_ds黑科技
- 策略路由(PBR policy based routing)
- Data Structures
- data structures
- Data Structures
- data structures
- Data Structures
- Data Structures
- Data Structures - Immutable Data Structures
- 平板电视(pb_ds)应用
- policy-based design
- Policy-Based Class Design
- Policy-based design
- Zoned-Based policy FW
- c++ Policy-based design
- c++教程(十五:Data structures)
- Nginx配置文件详细说明
- 为什么图片加载我首先Glide
- CocoaPods详解——安装和使用以及可能遇到的错误
- ViewPager源码解析
- ACM 训练计划
- pb_ds(Policy-Based Data Structures)
- 鸿洋大神的intentService
- 前台文字较多,增加悬浮层显示
- MyBatis小demo
- hashCode与equals的区别与联系
- 开方运算(求根)-- 中值定理
- opencv 读取图片(C/C++/python)
- android 4.2 系统以后的飞行模式
- mysql如何字段引用和防止出现数据库关键字