我知道性能从来不是非黑即白的,通常情况下某个实现在X情况下更快,在Y情况下更慢等,但总体而言,B树比AVL或RedBlack树更快吗?虽然它们比AVL树(甚至可能比RedBlack树)更复杂,但它们是否更快(它们的复杂性是否值得)?
编辑: 我还应该补充说,如果它们比等效的AVL / RedBlack树(以节点/内容为基准)更快-为什么它们更快?
我知道性能从来不是非黑即白的,通常情况下某个实现在X情况下更快,在Y情况下更慢等,但总体而言,B树比AVL或RedBlack树更快吗?虽然它们比AVL树(甚至可能比RedBlack树)更复杂,但它们是否更快(它们的复杂性是否值得)?
编辑: 我还应该补充说,如果它们比等效的AVL / RedBlack树(以节点/内容为基准)更快-为什么它们更快?
肖恩的文章(目前被接受的)包含了几个错误的观点。抱歉肖恩,我不是故意无礼;我希望我能说服你,我的陈述是基于事实的。
它们在使用情况上完全不同,因此不可能进行比较。
它们都用于维护一组具有快速查找、插入和删除的完全排序项。它们具有相同的接口和相同的意图。
红黑树通常是内存结构,用于提供对数据的快速访问(理想情况下为O(logN))。[...]
始终为 O(log n)
B树通常是基于磁盘的结构,因此比内存中的数据慢。
胡说八道。当您将搜索树存储在磁盘上时,通常会使用B树。这是真的。当您将数据存储在磁盘上时,访问速度比内存中的数据慢。但是存储在磁盘上的红黑树也比存储在内存中的红黑树要慢。
您正在进行不恰当的比较。真正有趣的是比较内存中的B树和内存中的红黑树。
[另外:与红黑树相反,B树在I/O模型中理论上是高效的。我已经对排序进行了实验测试(并验证了)I/O模型;我希望它也适用于B树。]
要清楚的是,B树节点的大小范围是树的一个参数(在C ++中,您可能希望使用整数值作为模板参数)。B树很少是二叉树,节点可以有许多子节点。
实际上,维基百科有一篇很棒的文章,展示了每个红黑树都可以轻松地表示为B树。以以下树形结构为例:
现在只需将其转换为B树(为了更明显,节点仍然是红/黑色的,这通常不是B树中的情况):
(由于某种奇怪的原因,无法在此处添加图像)
对于任何其他RB-Tree也是如此。它来自于这篇文章:
http://en.wikipedia.org/wiki/Red-black_tree
引用这篇文章的话:
红黑树在结构上等同于一个4阶B树,最小填充因子为每个簇的33%值,最大容量为3个值。
我没有找到任何数据表明其中一种显着优于另一种。如果是这样的话,我想其中一种已经消失了。它们在存储多少数据以及从树中添加/删除节点的复杂程度方面有所不同。
没有什么阻止一个仅在内存中运行的B-Tree实现。实际上,如果键比较便宜,内存中的B-Tree可以更快,因为它将多个键打包在一个节点中,这将在搜索期间导致较少的缓存未命中。请参见此链接进行性能比较。引用一句话:“速度测试结果很有趣,并表明对于包含超过16,000个项的树,B+树明显更快。”(B+树只是B-树的一种变体)。
这个问题虽然有点老,但我认为它仍然值得关注。Jonas Kölker和Mecki提供了非常好的答案,但我不认为这些答案涵盖了整个故事。事实上,整个讨论都错了 :-).
当条目相对较小(整数、小字符串/单词、浮点数等)时,关于B-树所说的是正确的。当条目变大时(超过100B),差异变得更小/无关紧要。
让我总结一下B-树的主要特点:
它们比任何二叉搜索树(BSTs)更快,因为它们具有内存局部性(减少了缓存和TLB失效)。
如果条目相对较小或条目大小不同,则B-树通常更节省空间。自由空间管理更容易(您可以分配更大的内存块),每个条目的额外元数据开销更低。B-树会浪费一些空间,因为节点并不总是满的,但它们最终仍然比二叉搜索树更紧凑。
大O性能(O(logN))对于两者都是相同的。此外,如果在每个B-树节点中进行二进制搜索,甚至会得到与BST中相同数量的比较(这是一个很好的数学练习来验证这一点)。 如果B-树节点大小合理(1-4倍缓存行大小),在每个节点内进行线性搜索仍然更快,因为硬件预取。您还可以使用SIMD指令来比较基本数据类型(例如整数)。
B-树更适合压缩:每个节点有更多的数据可压缩。在某些情况下,这可能是一项巨大的优势。 只需想象一下用于构建索引的关系型数据库表中的自动递增键。 B-树的前导节点包含连续的整数,非常容易压缩。
当存储在二级存储器上时(需要进行块IO时),B-树显然要快得多。
在理论上,B-树有很多优点,几乎没有缺点。那么是否应该只使用B-树以获得最佳性能呢?
答案通常是NO——如果树适合存储在内存中的话。在关键性能问题上,您需要一个线程安全的类似树形的数据结构(简单地说,多个线程可以完成比单个线程更多的工作)。使B-树支持并发访问比使BST支持并发访问更具问题性。使树支持并发访问最直接的方法是在遍历/修改节点时锁定它们。在B-树中,您需要锁定更多的每个节点条目,导致更多的串行化点和更多的争用锁。
所有版本的树(AVL、Red / Black、B-Tree和其他)都有无数个变体,它们之间的差异在于它们如何支持并发性。大学课程教授或从一些入门书籍中阅读的基本算法几乎从未在实践中使用。因此,很难说哪棵树的表现最好,因为没有关于每棵树背后准确算法的正式协议。我建议将提到的树更多地视为遵守某些树状不变量的数据结构类,而不是精确的数据结构。
以B-树为例。几乎从不使用原始B-树——您无法使其扩展得很好!最常用的B-树变体是B+ -Tree(广泛应用于文件系统、数据库)。 B+-Tree和B-Tree之间的主要区别:1)您不在树的内部节点中存储条目(因此,在修改存储在内部节点中的条目时,您不需要在树的高处写入锁定);2)您在同一级别的节点之间有链接(因此,在执行范围搜索时,您不必锁定节点的父节点)。
希望这可以帮助你。
对于某些应用程序而言,B树比BST快得多。 您可以在这里找到的树:
http://freshmeat.net/projects/bps
非常快速。它们还使用的内存比普通BST实现要少,因为它们不需要每个节点2或3个指针的BST基础设施,以及一些额外的字段来保持平衡信息。
它们都具有相同的渐近行为,因此性能更多地取决于实现而不是使用哪种类型的树。某些树结构的组合可能实际上是最快的方法,其中B树的每个节点恰好适合缓存行,并且使用某种二叉树在每个节点内部进行搜索。自己管理节点的内存也可能使您实现更大的高速缓存局部性,但代价非常高。
就我个人而言,我只使用我正在使用的语言的标准库中的任何内容,因为这需要很多工作,而获得的性能提升非常小(如果有的话)。
从理论上讲... RB树实际上与B树非常相似,因为它们模拟2-3-4树的行为。AA树是一种类似的结构,它模拟2-3树。
它们在不同的情况下被使用 - 当树节点需要在存储中保持在一起时,通常因为存储是磁盘页,所以重新平衡可能非常昂贵时使用B树。当您没有此约束时,使用RB树。因此,如果您想要实现(例如)关系数据库索引,则B树可能会更快,而如果您想进行内存搜索,则RB树可能会更快。