我知道性能从来都不是黑白的,通常情况下,一种实现在X情况下更快,而在Y情况下则慢,等等。但是总的来说,B树比AVL或RedBlack树快吗?与AVL树(甚至可能是RedBlack-tree)相比,它们的实现要复杂得多,但是它们是否更快(它们的复杂性是否有回报)?
编辑:我还要补充一点,如果它们更快,那么等效的AVL / RedBlack树(就节点/内容而言)-为什么它们更快?
Answers:
肖恩(Sean)的帖子(当前接受的帖子)包含多项不正确的声明。抱歉,肖恩,我不是无礼的。希望我能说服您我的陈述是事实。
它们的用例完全不同,因此无法进行比较。
它们都用于通过快速查找,插入和删除来维护一组完全有序的项目。它们具有相同的界面和相同的意图。
RB树通常是用于提供对数据的快速访问(理想情况下为O(logN))的内存结构。[...]
总是O(log n)
B树通常是基于磁盘的结构,因此其本质上比内存中的数据要慢。
废话。将搜索树存储在磁盘上时,通常使用B树。那是真的。当您将数据存储在磁盘上时,访问速度比内存中的数据要慢。但是,存储在磁盘上的红黑树也比存储在内存中的红黑树慢。
您在这里比较苹果和桔子。真正有趣的是比较内存中的B树和内存中的红黑树。
[顺便说一句:与红黑树相反,B树在I / O模型中理论上很有效。我已经通过实验测试(并验证了)用于分类的I / O模型;我希望它也适用于B树。]
B树很少是二叉树,一个节点可以拥有的子节点数量通常很多。
为了清楚起见,B树节点的大小范围是树的参数(在C ++中,您可能希望使用整数值作为模板参数)。
当数据更改时,B树结构的管理可能会非常复杂。
我记得它们比红黑树更易于理解(和实现)。
B树尝试最小化磁盘访问次数,以便合理地确定数据检索。
那是真的。
看到像4 B树访问这样的东西在一个非常大的数据库中查找一点数据的情况并不少见。
有数据吗?
在大多数情况下,我会说内存中的RB树更快。
有数据吗?
因为查找是二进制的,所以很容易找到东西。B树的每个节点可以有多个子节点,因此在每个节点上,您都必须扫描该节点以寻找合适的子节点。这是O(N)操作。
每个节点的大小都是固定参数,因此即使您进行线性扫描,它的大小也为O(1)。如果我们对每个节点的大小都比较大的话,请注意,通常您要对数组进行排序,使其为O(log n)。
在RB树上,它将是O(logN),因为您要进行一个比较然后进行分支。
您正在比较苹果和桔子。O(log n)是因为树的高度最多为O(log n),就像B树一样。
另外,除非您对红黑树玩弄讨厌的分配技巧,否则可以合理地推测出B树具有更好的缓存行为(它访问数组,而不是在各处散布的指针,并且分配开销较少,从而增加了内存)地方性),这可能有助于在速度竞赛中发挥作用。
我可以指出一些实验证据,即B树(大小参数分别为32和64)与小尺寸的红黑树相比非常有竞争力,甚至在n值适中时也优于B树。参见http://idlebox.net/2007/stx-btree/stx-btree-0.8.3/doxygen-html/speedtest.html
B树更快。为什么?我猜想这是由于内存局部性,更好的缓存行为和更少的指针追逐(如果不是同一件事,它们在某种程度上会重叠)。
实际上,维基百科上有一篇很棒的文章,它显示了每个RB-Tree都可以很容易地表示为B-Tree。以以下树为示例:
现在只需将其转换为B树(更明显的是,节点仍然是R / B色,这是您通常在B树中所没有的):
(由于某些奇怪的原因,无法在此处添加图片)
其他任何RB-树也是如此。摘自本文:
http://en.wikipedia.org/wiki/Red-black_tree
引用这篇文章:
然后,红黑树在结构上等同于4阶B树,其最小填充因子为每个群集值的33%,最大容量为3个值。
我没有发现任何数据表明两者之一都明显优于另一个。我想如果是这样的话,两者都已经消失了。对于必须在内存中存储多少数据以及从树中添加/删除节点的复杂程度,它们是不同的。
我的个人测试表明,B树在搜索数据时会更好,因为它们具有更好的数据局部性,因此CPU缓存可以比较快一些。B树的顺序越高(顺序是音符可以具有的子级数),查找就越快。另一方面,它们的顺序越高,添加和删除新条目的性能越差。这是由于在节点内添加值具有线性复杂度这一事实引起的。由于每个节点都是经过排序的数组,因此在向中间添加元素时,必须在该数组内移动很多元素:新元素左侧的所有元素必须向左移动一个位置,或者所有元素的右侧向右移动。新元素必须向右移动一个位置。如果在插入过程中某个值向上移动一个节点(这在B树中经常发生),则它会留下一个孔,该孔也必须通过将所有元素从左侧移到右侧或将所有元素移到右侧来填充。右边向左的一个位置。这些操作(通常由记忆体在C中执行)实际上为O(n)。因此,B树的顺序越高,查找速度越快,但修改速度越慢。另一方面,如果您选择的顺序太低(例如3),则B树在实践中与其他树结构相比几乎没有优势或劣势(在这种情况下,您也可以使用其他树)。因此,我总是会创建高阶的B树(至少4、8及以上是可以的)。它留下了一个孔,还必须通过将所有元素从左侧的一个位置向右移动或通过将所有元素向右的一个位置向左移动来填充孔。这些操作(通常由记忆体在C中执行)实际上为O(n)。因此,B树的顺序越高,查找速度越快,但修改速度越慢。另一方面,如果您选择的顺序太低(例如3),则B树在实践中与其他树结构相比几乎没有优势或劣势(在这种情况下,您也可以使用其他树)。因此,我总是会创建高阶的B树(至少4、8及以上是可以的)。它留下了一个孔,还必须通过将所有元素从左侧的一个位置向右移动或通过将所有元素向右的一个位置向左移动来填充孔。这些操作(通常由记忆体在C中执行)实际上为O(n)。因此,B树的顺序越高,查找速度越快,但修改速度越慢。另一方面,如果您选择的顺序太低(例如3),则B树在实践中与其他树结构相比几乎没有优势或劣势(在这种情况下,您也可以使用其他树)。因此,我总是会创建高阶的B树(至少4、8及以上是可以的)。另一方面,如果您选择的顺序太低(例如3),则B树在实践中与其他树结构相比几乎没有优势或劣势(在这种情况下,您也可以使用其他树)。因此,我总是创建具有高阶的B树(至少4、8及以上是可以的)。另一方面,如果您选择的顺序太低(例如3),则B树在实践中与其他树结构相比几乎没有优势或劣势(在这种情况下,您也可以使用其他树)。因此,我总是会创建高阶的B树(至少4、8及以上是可以的)。
通常基于B树的文件系统使用更高的阶数(阶数200甚至更多)-这是因为它们通常选择足够高的阶数,以使音符(包含允许的最大元素数时)等于硬盘驱动器上的扇区大小或文件系统群集的大小。这样可提供最佳性能(因为即使一次更改一个字节,HD一次只能写入一个完整的扇区,所以无论如何都将重写整个扇区)和最佳的空间利用率(因为驱动器上的每个数据条目至少等于该驱动器的大小)一个集群,或者是集群大小的倍数,无论数据实际多大)。由于以下事实导致:硬件将数据视为扇区,而文件系统将扇区分组为群集,B树比其他任何树结构都能为文件系统带来更好的性能和空间利用率。这就是为什么它们在文件系统中如此受欢迎。
当您的应用程序不断更新树,从树中添加或删除树中的值时,与高阶B树相比,RB树或AVL树平均表现出更好的性能。对于查询而言,情况稍差一些,它们可能还需要更多的内存,但是通常修改很快。实际上,RB-Tree的修改速度甚至比AVL-Tree快,因此AVL-Tree的查找速度要快一些,因为它们通常不那么深。
因此,与往常一样,它很大程度上取决于您的应用程序在做什么。我的建议是:
所有这些树的替代方法是AA树。正如该PDF文件所建议的那样,AA树(实际上是RB树的一个子组)在性能上与普通RB树几乎相同,但是比RB树,AVL树,或B树。这是一个完整的实现,请看它有多小(主要功能不是实现的一部分,而一半的实现行实际上是注释)。
如PDF纸所示,对于传统的树实现,Treap也是一种有趣的替代方法。Treap也是一棵二叉树,但是不会尝试强制平衡。为了避免最坏的情况,即您可能会遇到不平衡的二叉树(导致查找变为O(n)而不是O(log n)),Treap会给树增加一些随机性。随机性不能保证树平衡良好,但也极不可能使树极度不平衡。
没有什么可以阻止仅在内存中工作的B树实现。实际上,如果键比较便宜,则内存中的B-Tree可能会更快,因为它在一个节点中打包多个键会在搜索过程中减少缓存丢失。有关性能比较,请参见此链接。引言:“速度测试结果很有趣,并且对于包含16,000多个项目的树,B +树的速度显着提高。” (B + Tree只是B-Tree的一种变体)。
这个问题很老,但我认为它仍然有意义。乔纳斯·科克(JonasKölker)和梅基(Mecki)给出了很好的答案,但我认为答案并不能涵盖整个故事。我什至会争辩说,整个讨论都忽略了要点:-)。
当条目相对较小(整数,小字符串/单词,浮点数等)时,关于B树的说法是正确的。当条目较大(超过100B)时,差异变小/不明显。
让我总结一下有关B树的要点:
由于内存的局部性(导致更少的缓存和TLB丢失),它们比任何二进制搜索树(BST)都要快。
如果条目相对较小或条目的大小可变,则B树通常更节省空间。可用空间管理更容易(分配更大的内存),每个条目的额外元数据开销也较低。B-树将浪费一些空间,因为节点并不总是满的,但是,它们最终仍然比二叉搜索树更紧凑。
两者的大O性能(O(logN))相同。而且,如果您在每个B-Tree节点内进行二进制搜索,您甚至将得到与BST中相同的比较次数(验证这一点是一个很好的数学练习)。如果B树节点大小合适(1-4倍缓存行大小),则由于硬件预取,每个节点内部的线性搜索仍然会更快。您也可以使用SIMD指令比较基本数据类型(例如整数)。
B树更适合压缩:每个节点要压缩的数据更多。在某些情况下,这可能是一个巨大的好处。试想一下关系数据库表中用于建立索引的自动递增键。B树的前导节点包含非常好压缩的连续整数。
当存储在辅助存储(您需要执行块IO)时,B树显然快得多。
在纸上,B树有很多优点,几乎没有缺点。因此,应该只使用B树来获得最佳性能吗?
答案通常是“否”-如果树适合内存。在性能至关重要的情况下,您需要一个线程安全的树状数据结构(简单地说,多个线程可以完成比单个线程更多的工作)。使B树支持并发访问要比使BST更麻烦。使树支持并发访问的最直接方法是在遍历/修改节点时锁定它们。在B树中,您可以为每个节点锁定更多条目,从而导致更多的序列化点和更多竞争的锁定。
所有树版本(AVL,红色/黑色,B树等)都有无数的变体,它们在支持并发方面的方式有所不同。在大学课程中教授或从一些入门书籍中阅读的原始算法几乎从未在实践中使用。因此,很难说哪棵树表现最好,因为关于每棵树背后的确切算法尚无官方协议。我建议考虑的树更像是遵循某些树状不变式的数据结构类,而不是服从精确的数据结构。
以B树为例。香草B树几乎从未在实践中使用过-您无法使其缩放好!最常用的B-Tree变体是B + -Tree(广泛用于文件系统,数据库)。B +-树和B-树之间的主要区别是:1)您不将条目存储在树的内部节点中(因此,当修改存储在内部节点中的条目时,不需要树中的写锁高) ; 2)您在同一级别的节点之间具有链接(因此在进行范围搜索时不必锁定节点的父级)。
我希望这有帮助。
Google的专家们最近发布了他们的STL容器实现,该容器基于B树。他们声称,与通过红黑树实现的标准STL容器相比,它们的版本更快并且消耗的内存更少。在这里更多细节
对于某些应用程序,B树比BST快得多。您可能会在这里找到的树木:
http://freshmeat.net/projects/bps
很快 它们还比常规BST实现使用更少的内存,因为它们不需要每个节点2或3个指针的BST基础结构,以及一些额外的字段来保持平衡信息。
它们在不同的情况下会被使用-B树需要在存储中将树节点保持在一起时使用-通常是因为存储是磁盘页面,因此重新平衡可能会非常昂贵。当您没有此约束时,将使用RB树。因此,如果要实现(例如)关系数据库索引,B树可能会更快,而对于(例如)内存中搜索,RB树可能会更快。
它们都具有相同的渐近行为,因此,性能更多地取决于实现,而不是所使用的树类型。树结构的某种组合实际上可能是最快的方法,其中B树的每个节点正好适合高速缓存行,并且某种二叉树用于在每个节点内搜索。自己管理节点的内存也可能使您能够实现更大的缓存位置,但价格很高。
就我个人而言,我只是使用标准库中所用语言的内容,因为这需要大量工作才能获得很小的性能提升(如果有的话)。
从理论上讲... RB树实际上与B树非常相似,因为它们模拟了2-3-4树的行为。AA树是类似的结构,它可以模拟2-3棵树。