前两天叉姐的浣熊群里有人提到了pb_ds库这样一个东西,于是就去查了查,发现似乎还挺好玩的。鉴于网上pb_ds库的中文资料少得可怜,我也就简单整理下。

  pb_ds库大概是GNU对C++的一个扩展库,地位上必然是不如TR1这种基本成为官方标准的扩展库,但也是G++编译器默认附带的库。我在少数几个OJ上做了测试,CF和SPOJ都可以成功编译,但POJ和HDU都找不到头文件令我大失所望(事实上经我测试,连TR1的扩展如unordered_map都无法支持,我估计boost库也全都无法使用)。其实我是寄希望于在ACM现场赛可以使用……

  pb_ds库全称是Policy-Based Data Structures,可见是一些数据结构的集合,主要是Hash表,平衡二叉树、Trie树,优先队列(堆)等。英文官方文档传送门,里面有对各种数据结构的接口说明以及部分操作的性能测试。我现在只是试用了不多的几种结构,先写出来好了。

  优先队列(Priority Queue)

  我们知道在标准模板库(STL)中自带了priority_queue,在相当意义上替代了堆(Heap)的作用(我会说我已经好久没有手写堆了么)。但是在某些情况下,我们发现它并不能很好地应对某些需求,尤其是在一些图论算法中。比方说堆优化的Dijkstra,需要对优先队列中的元素的值进行修改;比方说一些题目需要对若干个堆进行合并。对于前者我们往往是记录了每个点对应的当前最短dist,然后将每次更新都入队(换言之存在重复入队),出队的值与当前dist不同的元素直接忽略掉(见这份板子中的代码),其实是存在时空浪费的;对于后者我们往往要手写左偏树或斜堆等数据结构来代替STL优先队列。此外在一些情况下,我们对STL自带优先队列的速度并不满意,甚至对手写堆的速度也不满意,如BZOJ3040就需要手写斐波那契堆或配对堆(恶心题还是要找中学生的题库啊),但斐波那契堆的难写大家也都是明白的。

  所以,我们需要一个方便易用不需手打的替代品。pb_ds库基本上满足了这些需求。pb_ds库包含了配对堆(pairing_heap)、二叉堆(binary_heap)、二项堆(binomial_heap)、冗余计数二项堆(redundant-counter binomial_heap,没找到通用译名,故自行翻译)、经改良的斐波那契堆(thin_heap)。各项操作复杂度在这里都能看到,可见pairing_heap和thin_heap的理论效率非常之高,某些操作达到均摊O(1),正是上面那道题的上上之选。

  为了测试效率,我随机了50组数据规模为10^5的数据,按照哈夫曼树(Huffman Tree)的构建方式来测试push和pop操作的效率(不明白的话请看这一题)。相关代码和测试效率如下。

#include<cstdio>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;
using namespace __gnu_pbds;
int main() {//freopen("in.txt","r",stdin);//freopen("out.txt","w",stdout);int n,x;while(~scanf("%d",&n)) {/*** __gnu_pbds::priority_queue<int,greater<int>,pairing_heap_tag> pq;* 3.0s~3.3s*//*** __gnu_pbds::priority_queue<int,greater<int>,binary_heap_tag> pq;* >>5min*//*** __gnu_pbds::priority_queue<int,greater<int>,binomial_heap_tag> pq;* 3.6s~4.5s*//*** __gnu_pbds::priority_queue<int,greater<int>,rc_binomial_heap_tag> pq;* 4.4s~4.6s*//*** __gnu_pbds::priority_queue<int,greater<int>,thin_heap_tag> pq;* 5.8s~6.0s*/__gnu_pbds::priority_queue<int,greater<int> > pq;while(n--) {scanf("%d",&x);pq.push(x);}int ans=0;while(pq.size()>1) {int x=pq.top();pq.pop();int y=pq.top();pq.pop();ans+=x+y;pq.push(x+y);}printf("%d\n",ans);}
}

  首先pb_ds库统一使用__gnu_pbds命名空间,这和hash_map使用__gnu_cxx命名空间是类似的。声明部分比较奇怪,经我测试必须加上__gnu_pbds::前缀,我想这和我同时使用了std命名空间,导致编译器无法确认应该使用STL还是pb_ds库导致的。第二个参数是比较函数,和STL的priority_queue类似,默认是less<Type>,第三个参数代表使用的堆类型,默认为pairing_heap_tag。

  这次测试每种堆都运行了5遍,记录时间的上下界;此外STL的priority_queue所用时间大致是5.4s~6.2s,我手写的堆(用数组模拟,使用位运算的普通二叉堆)所用时间大致是1.2s~1.4s。有个很奇怪的地方是pb_ds库的binary_heap的效率令人发指地低下,不明原因。总而言之,pairing_heap最具可用性。

  此外pb_ds库的优先队列支持迭代器,声明方法是:

__gnu_pbds::priority_queue<int>::point_iterator it;

  然后就可以按照一般的迭代器使用了;pb_ds库的push操作是有返回值的(与STL不同),返回的类型就是迭代器,这样用一个迭代器数组保存所有push进优先队列的元素的迭代器,就可以随时修改优先队列内部元素了。

  此外,pb_ds库的优先队列支持合并操作,pairing_heap的合并时间复杂度是O(logn)的,可以说基本上完美代替了左偏树。合并的调用方式是:

__gnu_pbds::priority_queue<int> a,b;
// 对两优先队列进行一些操作
a.join(b);

  此时优先队列b内所有元素就被合并进优先队列a中,且优先队列b被清空。

  很遗憾我查看了文件,发现了public方法里有用于分离的split函数,但split函数需要两个参数,我始终没有找到第一个参数的含义是什么,也就暂时无法使用split函数了。何况对于堆而言,分离的意义并不是十分明确,实用性并不大,也就不再探究了。

  平衡二叉树(Balanced Binary Tree)

  其实这才是更让我感兴趣的东西。在刷题过程中我们经常使用到set和map,也知道它们的内部实现是红黑树,但我们显然无法按照操作平衡二叉树的方式操作它们,甚至连平衡二叉树最基本的求kth和求rank操作都无法在O(logn)内完成。好在pb_ds库给了一个并不是很强大,但在一些时候足够用的解决方案。

  pb_ds库这次内置了红黑树(red-black tree)、伸展树(splay tree)和排序向量树(ordered-vector tree,没找到通用译名,故自行翻译)。这些封装好的树都支持插入(insert)、删除(erase)、求kth(find_by_order)、求rank(order_of_key)操作,于是我找来正好需要这四种操作的一道题SPOJ3273进行效率测试。

#include<cstdio>
#include<ext/pb_ds/assoc_container.hpp>
using namespace std;
using namespace __gnu_pbds;
int main() {/*** spoj3273, G++4.3.2, 4.66s* tree<int,null_mapped_type,less<int>,rb_tree_tag,tree_order_statistics_node_update> bbt;*//*** spoj3273, G++4.3.2, TLE* tree<int,null_mapped_type,less<int>,splay_tree_tag,tree_order_statistics_node_update> bbt;*//*** spoj3273, G++4.3.2, TLE* tree<int,null_mapped_type,less<int>,ov_tree_tag,tree_order_statistics_node_update> bbt;*/tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update> bbt;int n,num;char c;scanf("%d",&n);while(n--) {scanf(" %c%d",&c,&num);switch(c) {case 'I':bbt.insert(num);break;case 'D':bbt.erase(num);break;case 'K':num<=bbt.size()?printf("%d\n",*bbt.find_by_order(num-1)):puts("invalid");break;case 'C':printf("%d\n",bbt.order_of_key(num));break;}}
}

  此外,我手写了Treap和Size Balanced Tree(代码在此)并经过测试,前者3.94s,后者4.06s,可见手写数据结构的速度总归要比封装好的要快,而封装好的实现中红黑树速度最快,这也是意料之中的。关于声明变量时的参数,第一个是键(key)的类型;第二个是值(value)的类型,null_type表示没有值,简单地理解就是表明这是set而不是map,注意SPOJ的G++版本稍旧(4.3.2),需要写成null_mapped_type才可以,我本地的G++版本为4.7.1;第三个表示比较函数,默认为less<Type>;第四个为平衡二叉树的类型,默认为红黑树rb_tree_tag;第五个代表元素的维护策略,只有当使用tree_order_statistics_node_update时才可以求kth和rank,此外还有null_tree_node_update(默认值)等,具体的区别在官方文档中有介绍(好吧我承认我没看懂= =)。

  这里要注意的是,求kth(find_by_order)返回的是迭代器,求rank返回的是值,两者都是从0开始计算的。

  此外,它们也支持合并(join)和分离(split)操作。用法如下。

tree<int,null_type> a,b;
// 对两平衡二叉树进行一些操作
a.join(b);
// 对两平衡二叉树进行一些操作
a.split(v,b);

  这里需要进行一些解释。join操作的前提是两棵树的key的取值范围不相交,否则会抛出一个异常;合并后平衡二叉树b被清空。split操作中,v是一个与key类型相同的值,表示key小于等于v的元素属于平衡二叉树a,其余的属于平衡二叉树b,注意此时后者已经存有的元素将被清空。

  嗯,目前就这些了,pb_ds库里其实还有关于hash_table和trie的,对于后者我表示有一点兴趣;此外这些天可能会研究一下rope,甚至挖掘一下说过好久的boost库的潜力。

随便玩了玩pb_ds库相关推荐

  1. 大学python选择题题库及答案_大学慕课用Python玩转数据题库及答案

    大学慕课用Python玩转数据题库及答案 更多相关问题 (19分)电解原理在化学工业中有广泛应用.右图表示一个电解池,装有电解液c :A.B是两块电极板,通过导线与直流 用铂电极电解CuCl2与CuS ...

  2. python单选题库答案_大学慕课2020用Python玩转数据题库及答案

    大学慕课2020用Python玩转数据题库及答案 更多相关问题 [单选] 补偿滑轮组中动滑轮槽偏转角度不大于(). [单选] 30型杵环杆长度是(). [单选] 上下行分段绝缘器的空气间隙应达到()及 ...

  3. python 题库app_中国大学MOOC的APP慕课2021用Python玩转数据题库及答案

    中国大学MOOC的APP慕课2021用Python玩转数据题库及答案 更多相关问题 Problématique (en français) Translation: Most importantly, ...

  4. 玩转 RTC时钟库 DS3231

    1.前言     接着博主的上一篇 玩转 RTC时钟库 + DS1302,这一篇我们重点讲解DS3231时钟模块.没有看过上一篇的同学,麻烦先去阅读一下,因为很多理论基础已经在上一篇做了详细讲解,这里 ...

  5. 用turtle作画玩一玩吧

    文章目录 turtle是什么 turtle的基本使用 几个小栗子 挺无聊的,假装自己是个小孩子,用turtle作画玩一玩吧,画个万花筒什么的. turtle是什么 turtle是海龟.它是python ...

  6. Egret QQ玩一玩适配【踩坑日记】

    需要申明一点,这是我接过最坑的渠道了,各种神奇的问题,首先是接口比较奇怪而且新旧版本搞得很混乱,其次是平台底层实现性能差而且很多限制.此外,这里需要理清楚一个概念:QQ 玩一玩 和 QQ 玩吧 并非同 ...

  7. QQ玩一玩(轻游戏)开发环境搭建与调试

    玩一玩开发环境搭建与调试 前言 Android 调试 下载Android Studio 配置环境变量 进入用户当前目录 设置环境变量 设置生效 检查配置是否正常 下载特殊版测试Q 调试时Android ...

  8. 白鹭引擎开发QQ玩一玩轻游戏之Windows配置开发环境

    准备工作: 1.了解玩一玩   https://hudong.qq.com/  (简单一句话:基于手机QQ内置的一款游戏引擎开发手游的环境) 2.需要下载的软件 本文介绍用白鹭开发游戏 再转换成玩一玩 ...

  9. 玩一玩游戏之Hello World

    环境搭建 下载引擎代码 Hello World 修改配置文件 需求实现 运行测试 手Q中测试 环境搭建 由于目前玩一玩开发平台仅支持Mac,所以你需要一台Mac电脑或者黑苹果(但不推荐) 1.注册苹果 ...

最新文章

  1. C++ #if、#elif、#else和#endif指令 的使用
  2. CUDA 并行计算优化策略总结
  3. Chmod 777到一个文件夹和所有内容[重复]
  4. 51单片机中变量的存储 xdata bdata idata pdata区别
  5. 阿里云MaxCompute香港开服 将引入更多人工智能服务
  6. Angular实现图片点击缩放组件
  7. Intel 64/x86_64/x86/IA-32处理器标志寄存器详解(5) - 32位EFLAGS - 系统标志
  8. 堆、栈、堆栈详谈--复习
  9. 老男孩Linux架构师实战课程14期教程
  10. 使用EXCEL进行线性回归
  11. 用74161设计十二进制计数器
  12. html修改按钮属性,button属性
  13. 算术练习题(java)
  14. lambda函数用法及注意事项(简单总结,有待补充)
  15. 拯救剧荒!程序员最爱看的美剧TOP5!
  16. aar打包依赖 android_打包依赖.aar文件以及坑总结
  17. Acer传奇Go电脑开机几分钟就会蓝屏怎么重装系统?
  18. 餐饮企业转型为大数据公司
  19. SharePoint Designer 2013 和 Visio 2013 中的工作流开发
  20. [HNOI2006]鬼谷子的钱袋

热门文章

  1. “Missing essential plugin: org.jetbrains.android……”
  2. 在使用eclipse的时候,一不小心关掉了下面的控制台,要这么做就能恢复
  3. 小米OJ 98(买香蕉)
  4. git 无法push远程仓库 Note about fast-forwards
  5. 【转】【中级财管精华】递延年金现值公式的理解
  6. 容联YOYO:国内漫游费取消 国际漫游如何玩?
  7. 黑盒测试中关键截图如何打点
  8. TimeSpan格式化
  9. 异步实现方式一:异步回调
  10. AAC格式音频的结构分析