在半连续列表的二进制搜索上使用二进制搜索树是否有现实原因?

Are there real-world reasons to employ a Binary Search Tree over a Binary Search of semi-contiguous list?

我正在看关于算法的大学讲座,似乎很多讲座几乎完全依赖某种特定类型的二叉搜索树来完成 querying/database/search 任务。

我不明白这种对二进制搜索的痴迷。似乎在 vast 大多数情况下,在静态数据的情况下,BSP 可以替换为排序数组,或者如果插入是动态发生的,则 BSP 可以替换为排序的桶列表,然后二进制搜索 可以用于它们。

使用这种方法,您可以获得与 BST 相同的算法复杂性(至少对于查询而言),方式更好的缓存一致性,更少的内存碎片(以及更少的 gc 分配取决于关于你使用的语言),并且可能更容易编写。

根本问题是 BSP 完全是内存朴素的——他们的重点是完全 O(n) 复杂度,他们忽略了内存碎片和缓存的非常实际的性能考虑连贯性...我错过了什么吗?

二叉搜索树 (BST) 并不完全等同于建议的数据结构。当动态插入和删除排序值时(假设它们正确平衡),它们的渐近复杂性更好。例如,当您何时动态构建 top-k 值的索引时:

while end_of_stream(stream):
    value <- stream.pop_value()
    tree.insert(value)
    tree.remove_max()

由于线性时间插入,排序数组在这种情况下效率不高。分桶列表的复杂性并不比普通列表渐近好,而且还受到线性时间搜索的影响。可以注意到在这种情况下可以使用堆,实际上在这里使用堆可能更好,尽管它们并不总是可以互换的。

话虽这么说,你是对的:BST 很慢,会导致大量缓存未命中和碎片化等。因此,它们经常被 B-trees 等更紧凑的变体所取代。 B 树使用排序数组索引来减少节点跳转的数量并使数据结构更加紧凑。它们可以与一些 4 字节指针优化混合使用,使它们更加紧凑。 B 树之于 BST 就像分桶链表之于普通链表。 B 树非常适合为存储在慢速存储设备上的巨大数据集构建动态数据库索引(由于大小):它们使应用程序能够使用非常很少的存储设备查找[来获取与键关联的值。 =24=](例如在 HDD 上非常慢)。真实世界用例的另一个例子是区间树。

请注意,可以使用压缩方法减少内存碎片。对于 BSTs/B-trees,可以像在堆中一样对根节点重新排序。然而,压缩并不总是容易应用,尤其是在像 C/C++ 这样带有指针的本地语言上,尽管有些 very clever methods exists 这样做。

请记住,B 树仅适用于大数据集(尤其是那些不适合缓存的数据集)。对于相对较小的,仅使用普通数组甚至排序数组通常是一个很好的解决方案。