这里有很多热量,但光线不多,所以让我们看看我们是否可以提供一些。
首先,RB树是一种关联数据结构,不像数组,它不能接受一个键并返回一个关联的值,除非那是一个0中的整数“键” % 连续整数的稀疏索引。数组的大小也不能增长(是的,我也知道 realloc(),但在幕后需要一个新数组,然后是 memcpy()),所以如果你有这些要求中的任何一个,数组不会做.数组的内存效率是完美的。零浪费,但不是很聪明或灵活 - realloc() 无法承受。
第二,与元素数组上的 bsearch() 相比,bsearch() 是一种关联数据结构,RB 树可以动态增长(和缩小)自身的大小。 bsearch() 可以很好地索引已知大小的数据结构,该数据结构将保持该大小。因此,如果您事先不知道数据的大小,或者需要添加或删除新元素,则使用 bsearch()。 Bsearch() 和 qsort() 在经典 C 中都得到了很好的支持,并且具有良好的内存效率,但对于许多应用程序来说不够动态。不过,它们是我个人的最爱,因为它们快速、简单,而且如果您不处理实时应用程序,它们通常足够灵活。此外,在 C/C++ 中,您可以对指向数据记录的指针数组进行排序,例如,指向您希望比较的 struc{} 成员,然后重新排列指针数组中的指针,以便按顺序读取指针在指针排序的末尾按排序顺序生成您的数据。将其与内存映射数据文件一起使用是非常节省内存、快速且相当容易的。您需要做的就是在比较函数中添加几个“*”。
第三,与哈希表不同,哈希表也必须是固定大小,一旦填满就不能生长,RB树会自动生长并平衡自身以维持其O(log( n)) 性能保证。特别是如果 RB 树的键是 int,它可以比散列更快,因为即使散列表的复杂度是 O(1),1 可能是一个非常昂贵的散列计算。一棵树的多个 1 时钟整数比较通常优于 100 时钟 + 哈希计算,更不用说重新散列,以及用于散列冲突和重新散列的 malloc()ing 空间。最后,如果您想要 ISAM 访问以及对数据的密钥访问,则排除哈希,因为哈希表中没有固有的数据排序,这与任何树实现中的数据自然排序相反。散列表的典型用途是为编译器提供对保留字表的键控访问。它的内存效率非常好。
第四,在任何列表中都非常低,是链表或双向链表,与数组相比,它自然支持元素插入和删除,这意味着调整大小.它是所有数据结构中最慢的,因为每个元素只知道如何到达下一个元素,因此您必须平均搜索 (element_knt/2) 链接才能找到您的数据。它主要用于列表中间某处的插入和删除很常见的地方,尤其是列表是循环的并且提供昂贵的过程的地方,这使得读取链接的时间相对较短。如果您唯一的要求是能够增加大小,我的一般 RX 是使用任意大的数组而不是链表。如果数组的大小用完了,可以重新分配()更大的数组。当您使用向量时,STL 会“在幕后”为您执行此操作。粗略,但如果您不需要插入、删除或键控查找,可能会快 1,000 倍。它的内存效率很差,尤其是对于双向链表。实际上,需要两个指针的双向链表与红黑树一样具有内存效率低下的特性,但没有任何吸引人的快速、有序的检索特性。
第五,与其他任何数据结构相比,树支持对其排序数据的许多额外操作。例如,许多数据库查询利用这样一个事实,即可以通过指定它们的公共父级轻松指定一系列叶值,然后将后续处理集中在父级“拥有”的树的部分上。这种方法提供的多线程潜力应该是显而易见的,因为只需要锁定树的一小部分区域——即只有父节点拥有的节点和父节点本身。
简而言之,树是数据结构的凯迪拉克。你在使用的内存方面付出了高昂的代价,但你得到了一个完全自我维护的数据结构。这就是为什么,正如这里的其他回复所指出的,事务数据库几乎完全使用树。