【GXZ的原创】平衡树性能测试

本文作者为 GXZlegend ,转载请注明 出处 ,谢谢!


〇、序言

前些日子闲的蛋疼做了个平衡树性能测试。。。

主要是因为学会的平衡树越来越多,做题时却不知道写哪个。。。

本想结合效率和代码复杂度来决定通常情况下写哪个,

然而在网上没有查到有关平衡树性能的相关信息(也许是我查的姿势不太对吧。。。)

于是最终决定自己动手测试,造福自己和他人,毕竟时间是检验真理的唯一标准嘛。。。

废话不多说,直接开始~

一、测试成员(按字典序从小到大排序)

Multiset

SBT

Splay

Treap

替罪羊树

权值线段树+动态开点

权值线段树+离线离散化

非旋转Treap

另外为了卖萌还加入了BST(逃

二、测试题目

支持$10^6$次插入一个$[-10^7,10^7]$范围内的数、删除一个当前序列中的数。

这里之所以没有选择各种各样的查询操作,是因为平衡树的瓶颈不在于一般BST的查询操作。只有插入和删除才能体现出平衡树的效率。

三、测试数据

共放了12组测试数据。

其中1、2号数据是随机数据(1号只有插入,2号在插入后删除掉);

3~8号数据是按某一特定顺序将1~n(或n/2)这些数插入(并删除);

9号数据是插入1~n/2后不断插入并删除$10^7$&$-10^7$;

10、11号数据是插入1、-1、2、-2…和n/2、-n/2、n/2-1、-(n/2-1)…;

12号数据是插入n个1。

具体的数据可以在下面的测试包中查看。

四、测试结果

测试环境为GXZ的电脑

使用Win10系统,g++4.8.4,并在Lemon下进行评测。评测结果如下:

注:总时间不具有非常大的参考价值!

注:总时间不具有非常大的参考价值!

注:总时间不具有非常大的参考价值!

开O2:

不开O2:

 

五、具体描述与分析(顺序为不开O2总时间倒序)

1.非旋转Treap真心慢。。。而且最慢的是随机数据1、2号点,其中只有插入的1号点更慢一些。与其功能相似的Splay比其发挥更好一些。构造数据的效率还好(其中11号点稍微慢一些)。非常吃数据。

2.替罪羊树作为倒数第二可能不服,不过事实上替罪羊树在随机数据上表现良好,而在构造数据上表现得稍微差一些。总体上不是很吃数据。

3.权值线段树+离散化在不开O2情况下很慢,但是开O2情况下效率仅次于权值线段树+动态开点。因此其效率取决于排序的复杂度,不吃数据。

4.Multiset效率和想象中一样,开O2时飞起,不开O2时奇慢。。。(想到我的NoiD1T2,哈希非要多个map,白白挂了40' QAQ)不吃数据。

5.Splay在随机数据上效率极低(仅略优于非旋转Treap),而在构造数据上跑得飞快。具体原因是在插入时把插入的数旋转了上去,使得下次插入复杂度变为近似$O(1)$。非常吃数据。

6.Treap在随机数据上较慢,其余效率较高。吃数据。(另外一个小插曲,在Linux下Treap非常快,效率接近于权值线段树+动态开点,猜想可能与随机数的范围有关。然而我在Windows下改用了rand()*rand()却发现更慢了。。。)

7.SBT随机数据比Treap略快,构造数据比Treap略慢。。。略吃数据。

8.权值线段树+动态开点是最快的“平衡树”,所有数据都较快,略吃数据。

9.BST不解释,随随便便即可卡掉。

六、对比总结

对于任何情况,权值线段树都是最快的,并且也是最好写的。它的唯二缺点:不支持定点插入、空间消耗较大(作为树套树内层时多1个log)。但是它的高效以及支持多棵同时二分,使得它的用途非常之广泛,大多数情况可以代替Treap和SBT。

SBT和Treap的效率不会差很多,因此不必纠结于学哪种,掌握较简单的就行(个人倾向于SBT一些)

在不开O2情况下少用set,开O2时可以大胆使用。

尽量不直接使用替罪羊树,个人建议替罪羊树的唯一使用情况就是放到树套树的外层(无旋转避免了pushup)

Splay不要用于维护全序集,最好用于维护有序序列,效果较好。

非旋转Treap效率真的不如Splay,唯一好处就是时间复杂度非均摊,可以可持久化

七、测试包下载

https://pan.baidu.com/s/1geQGMEf

希望大家再写平衡树时就有一个参考辣~

原文地址:https://www.cnblogs.com/GXZlegend/p/7565067.html