如何从给定列表有效地构造 B+ 树?

Posted

技术标签:

【中文标题】如何从给定列表有效地构造 B+ 树?【英文标题】:How to efficiently construct a B+ tree from a given list? 【发布时间】:2015-01-02 19:54:55 【问题描述】:

我想从给定的大小为 N 的无序元素列表构建 B+ 树。

我知道这样做的最佳界限是Θ(N / B * logM / B(N / B)) 块转移,这也是排序的最佳选择;所以我不能简单地选择一个项目并单独在树中插入,因为它会给我O(N logB(N)) 块传输。

所以我认为构建树的最佳方法是首先对元素进行排序,因为无论如何叶子都是有序的。从此,我不知所措了。

我想过这样的事情:

    从列表中取出B元素 按它们在某处写它们(它是三者中的一片) 取块的最后一个元素(最大);它将是叶子父节点的路由键 对下一个元素重复步骤 1,直到父元素中有 B-1 路由键 当父节点中有B-1 路由键时,表示已满。所以新的路由键将改为“祖父”(因此树增长了一层),并且所有新叶子都会有一个新的父节点 这样继续下去,直到 N/B 块被读取

基本上,问题在于我没有考虑内部节点可以拥有的最小子节点数。因此,例如一个节点最终可能只有一个孩子,这显然是错误的。

我到处寻找,但我找不到实际上解释如何在Θ(N / B * logM / B(N / B)) 中构建树的算法。我发现的只是将列表中的每个项目简单地插入到树中的算法,而没有利用 B 因素。

你能帮帮我吗,也许能给我指明正确的方向?

【问题讨论】:

关于第5步,“新的路由密钥将转到祖父”,进入祖父的不是新密钥,而是老父亲的中间密钥。新钥匙进入新父亲 你是对的,我的错!如果我在内部节点到达 B-1 元素时拆分它们,我可以保证树的构建尊重每个节点的最小子节点数。谢谢! 【参考方案1】:

我认为我不会同时构建所有关卡,这可能会使用超过恒定数量的 RAM 块,而是构建从最叶到最根的关卡(即广度优先而不是深度优先) )。给定列表,将其贪婪地切割成大小为 B 的块。如果只有一个块,那就是根。否则,如果最后一个块的元素太少,则尽可能将其元素与倒数第二个块的元素重新平衡;现在两者都将有足够的元素。下一个列表由该级别每个块中的最后一个元素组成。

【讨论】:

如何在第一个和下一个块集之间建立关系?我想第一个块集应该仍然在内存中以完成该操作 根据 mangusta 的建议,我认为可以在主内存中仅使用 h + 1 个大小为 B 的块来完成(其中 h 是树的高度); 1个块用于从列表中加载B元素(基本上是叶子),另一个h(每个级别1个块)用于存储路由键。当 h 块中的一个已满时,将其拆分:第一层 ((B+1)/2) 个元素可以进入外部内存,因为它们不会再次使用,位置 ceil((B+1) 的元素/2) 进入与上一级相关的主内存块中,块中的剩余元素保留在那里 @Beriol 你的方式需要大量的 I/O 操作,所以我们在这里进行权衡 这是为什么呢?我的意思是,有不可避免的 N/B I/O 操作,加上树中每个节点的一个 I/O 操作,也是不可避免的……还是不是? @Beriol 正如 David Eisenstat 所建议的那样,一级 = 一次写入,同一级别中的所有块都驻留在同一个列表中,因此同一级别中的所有块都被一次写入。在您的情况下,您必须为每个块执行写入

以上是关于如何从给定列表有效地构造 B+ 树?的主要内容,如果未能解决你的问题,请参考以下文章

如何有效地获得给定范围内的除数之和?

如何在给定索引列表的情况下有效地更新 numpy ndarray

如何从特定节点获取树祖先列表?

如何优雅地构造在 C 中遍历数组的长参数列表

给定两个 2D numpy 数组 A 和 B,如何有效地将采用两个 1D 数组的函数应用于 A 和 B 行的每个组合?

如何有效地将具有一定周期性的列表拆分为多个列表?