【问题标题】:Finding the median in B+ tree在 B+ 树中找到中位数
【发布时间】:2013-05-11 15:55:05
【问题描述】:

我需要实现一个 B+ 树。

我需要创建以下方法:

  1. 插入(x) - 0(log_t(x))。
  2. 搜索 - 成功搜索 - O(log_t(x))。搜索不成功 - O(1) {具有高可能性}

所以我开始实现 Insert(x) - 每次我有一个完整的叶子时,我都想把它分成两个分开的叶子。 一个叶子的键等于或低于中值键,第二个叶子将包含值高于中值的键。

如何在不影响运行时间的情况下找到这个中值?

我想过:

  1. 将每个内部节点和叶子表示为较小的 B+ 树,但只有当树完全平衡时,中值才是根(或根中的元素之一)。
  2. 将每个内部节点和叶子表示为双向链表。并在插入输入时尝试获取中间键,但是有输入无法使用。
  3. 表示为数组可能会给我中间,但是当我将其拆分时,我至少需要 O(n/2) 才能将键插入新数组。

我能做什么?

关于搜索,Idea-wise:成功和不成功搜索之间的区别在于在叶子中搜索,但我仍然需要“运行”树的不同键以确定键是否在树。那怎么可能是O(1)呢?

【问题讨论】:

    标签: java algorithm


    【解决方案1】:

    在 B+ 树中,所有值都存储在叶子中。

    请注意,您可以将每个叶子的指针添加到下一个叶子,除了标准 B+ 树之外,您还可以获得一个包含所有元素的有序链表

    现在,请注意,假设您知道此链表中的当前中位数是多少 - 在插入/删除时,您可以廉价地计算新的中位数(它可以是同一个节点、下一个节点或上一个节点,没有其他选择)。
    注意修改这个指针是O(1)(虽然插入/删除本身是O(logn)

    鉴于这些知识 - 可以缓存一个指向中间元素的指针,并确保在删除/插入时对其进行维护。当您要求中位数时 - 只需从缓存中获取中位数 - O(1)


    关于Unsuccessful search - O(1) {With a high likely-hood} - 这个尖叫bloom filters,这是一个概率集合实现,它永远不会有假阴性(永远不会说某物不在集合中),但有一些误报(说某些东西在缓存中,而实际上没有)。

    【讨论】:

    • 值存储在叶子中。但是键在树的内部节点中是如何排序的呢? (不是叶子)
    • 密钥也存储在叶子中。将 B+ 树视为一个集合或多集合,所有的键都存储在叶子中。内部节点仅保存部分键以导航到叶子。请注意,集合中没有不存在于所有叶子中的键(即,当且仅当它存在于树的一个叶子中时,一个项目才存在于 B+ 树表示的集合中)。
    【解决方案2】:

    您不需要 B+-树的中位数。您需要要拆分的节点中的中值键。您必须在该中位数处拆分以满足每个节点具有 N/2 个键的条件。一个节点中的中间键就是中间的那个,在 n/2 处,其中 n 是节点中 实际 个键的数量节点。那就是您拆分节点的地方。 O(1) 的计算:它不会损害运行时。

    如果不叠加另一个数据结构,您无法从 B+-tree 获得 O(1) 的搜索失败时间。

    【讨论】:

      【解决方案3】:

      我已经发布了一个答案(并且已经删除了它),但我可能误解了,所以这里是另一种解释的答案......

      如果您需要始终知道哪个项目是完整 B+ 树容器中的中位数。

      正如阿米特所说,您可以保留一个指针(连同您的根指针)指向包含中位数的当前叶节点。您还可以在该叶节点中保留索引。因此,您可以通过直接跟踪正确的节点和项目来获得 O(1) 访问权限。

      问题在于保持这一点。当然阿米特是正确的,对于每个插入,中位数也必须保持相同的项目,或者必须步进到之前或之后的那个。如果你有一个通过叶节点的链表,即使这意味着要跳到相邻的叶节点,也可以有效地处理。

      不过,我不相信,除了中值和插入恰好在同一个叶节点中的特殊情况外,确定是否或采用哪种方式是微不足道的。

      如果您知道完整树的大小(您可以使用根指针轻松存储和维护),您至少可以确定插入前后中间项应该位于哪个索引。

      但是,您需要知道前一个中值项的索引是否通过插入向上移动 - 插入点是在中值之前还是之后。除非插入点和中位数恰好在同一个节点中,否则这是个问题。

      矫枉过正的方式 - 扩充 B+ 树以支持计算项目的索引和搜索索引。这样做的诀窍是每个节点都保留其子树的叶节点中的项目总数。这可以提升一个级别,因此每个分支节点都有一个子树大小数组及其子节点指针数组。

      这提供了两种解决方案。您可以在搜索时使用该信息来确定插入点的索引,或者(如果节点具有父指针)您可以使用它来重新确定插入后前一个中间项的索引。

      [实际上是三个。插入后,可以根据新的大小直接搜索新的中途索引,无需参考之前的中间链接。]

      然而,就为增强而存储的数据而言,这被证明是矫枉过正的。您不需要知道插入点的索引或先前的中位数 - 您可以知道插入发生在中位数的哪一侧。如果您知道从根到中间项的路径,您应该能够在搜索插入点时跟踪您所在的那一侧。因此,您只需增加足够的信息即可找到并维护该线索。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-10-29
        • 1970-01-01
        • 2016-05-08
        • 1970-01-01
        • 1970-01-01
        • 2015-05-11
        相关资源
        最近更新 更多