我需要实现一个B +树。
我需要创建以下方法:
所以我开始实现Insert(x) - 每次我有一个完整的叶子我想把它分成两个分开的叶子。 一个键的键等于或低于中间键,第二个将包含值高于中值的键。
如何在不损害运行时间的情况下找到此中位数?
我想到了:
我该怎么办?
关于搜索,想法明智:成功和不成功搜索之间的区别在于在树叶中搜索,但我仍然需要通过树的不同键“运行”以确定密钥是否在树。那怎么可能是O(1)?
答案 0 :(得分:4)
在B +树中,所有值都存储在树叶中。
请注意,您可以将每个叶子的指针添加到下一个叶子,除了标准B +树之外,您还可以获得包含所有元素的有序链接列表。
现在,请注意,假设您知道此链接列表中的当前中位数是什么 - 插入/删除时您可以便宜地计算新的中位数(它可以是同一节点,下一个节点或上一个节点,没有其他选择)。
请注意,修改此指针为O(1)
(尽管插入/删除本身为O(logn)
。
鉴于知识 - 可以缓存指向中值元素的指针,并确保在删除/插入时保留它。当您要求中位数时 - 只需从缓存中获取中位数 - O(1)
。
关于Unsuccessful search - O(1) {With a high likely-hood}
- 这个是尖叫 bloom filters ,这是一个概率集实现,从不会出现假阴性(从来没有说过某些东西没有设置) ,但有一些误报(说某些东西在缓存中,而事实上并非如此)。
答案 1 :(得分:2)
您不需要B + -tree的中位数。您需要在要拆分的节点中使用中值键。您必须在该中值处拆分以满足每个节点具有 N / 2< = n< = N 键的条件。节点中的中间键只是中间的键,位于 n / 2 ,其中 n 是中实际键的数量。节点。这就是你拆分节点的地方。计算是O(1):它不会伤害运行时。
在不叠加其他数据结构的情况下,您无法从B +树获得O(1)搜索失败时间。
答案 2 :(得分:1)
我已经发布了一个答案(并且已经删除了它),但我可能会误解,所以这里是另一种解释的答案......
如果您需要始终知道哪个项目是完整 B +树容器中的中位数,该怎么办。
正如amit所说,你可以将指针(以及你的根指针)保存到包含中位数的当前叶节点。您还可以在该叶节点中保留索引。因此,您可以通过直接关注正确的节点和项目来获得O(1)访问权限。
问题在于维持这一点。当然amit是正确的,对于每个插入,中位数也必须保持相同的项目,或者必须步骤到之前或之后的那个。如果您通过叶节点有一个链表,即使这意味着步入相邻的叶节点,也可以有效地处理。
但是,我不相信,尽管如此,确定是否或采用哪种方式,除了在中位数和插入恰好位于同一叶节点的特殊情况之外,这是微不足道的。如果您知道完整树的大小(可以使用根指针轻松存储和维护),则至少可以确定插入前后中间项应该是哪个索引。
但是,您需要知道先前的中间项是否已将其索引向上移位 - 如果插入点位于中位数之前或之后。除非插入点和中位数恰好位于同一节点,否则这是一个问题。
Overkill方式 - 扩充B +树以支持计算项目的索引并搜索索引。其诀窍是每个节点保留其子树的叶节点中的项目总数。这可以推高一个级别,因此每个分支节点都有一个子树大小数组及其子节点指针数组。
这提供了两种解决方案。您可以使用该信息在搜索时确定插入点的索引,或者(提供节点具有父指针)您可以使用它来重新确定插入后的上一个中间项的索引。
[实际上是三个。插入后,您可以根据新的大小搜索新的中途索引,而不参考之前的中间链接。]
然而,就增加存储的数据而言,这结果是过度的。您不需要知道插入点的索引或先前的中位数 - 您可以知道插入的中位数的哪一侧。如果您知道要从根到中间项的跟踪,您应该能够在搜索插入点时跟踪它的哪一侧。因此,您只需要增加足够的信息来查找和维护该踪迹。