B-树插入:在树的下降过程中,为什么我们用 2t-1 个元素拆分每个节点?

B-tree insertion: during the descend in the tree, Why we split every node with 2t-1 elements?

在B-tree插入算法中,我看到为了解决我们需要向一个有2t-1个元素的叶子插入一个元素的情况,我们需要对树进行分裂算法。我不明白的是为什么在树下降(到愿意点)期间的插入算法中我们用 2t-1 个元素拆分每个节点,即使我看起来没用。例如 example

我知道有一种情况,叶子上方的几个节点有 2t-1 个元素,如果我们想将中值移动到它们,我们会遇到问题,但为什么不为此给出精确的解决方案,而不是每次都拆分。

如果我说错了请指正。

我们在到达目标位置的过程中拆分了完整的节点,因为我们不知道是否需要 "go back up." 你 可以 这样做你在想,我们向下找到目标节点,将其拆分,然后将拆分的中值插入父节点,根据需要递归拆分节点。但这需要我们从根开始,向下到目标,然后返回,可能一直到根。这可能是不可取的,例如如果访问节点两次会太昂贵。在这种情况下,最好直接向下传递一次,将所有完整节点拆分为 预期 需要更多 space.

为了演示,您可以尝试将 10 插入绘图中间和底部的树中。底部的树,未分裂,需要像中间树一样一直分裂到根,因为两次遍历算法没有留下任何space。在中间的树中,插入10仍然会导致分裂,但不会一直向上延伸,因为树的顶部两层非常宽敞。

不过,有一个重要的警告。设 t 为每个节点的最小子节点数。对于两次通过算法,一个节点可以拥有的最大子节点数至少需要 u = 2t - 1。如果少了,比如2t - 2,那么分裂一个全节点(2t - 3个元素),即使再插入额外的元素,也做不出两个非亏节点。一次通过算法需要更高的最大值,u = 2t。这是因为两次通过算法总是有一个元素可以恰好抵消一个缺陷。一次通过算法没有这种能力,因为它有时会不必要地分裂节点,所以它不能将它持有的元素粘在其中一个缺陷中。它可能不属于那里。

我已经实现了好几次B-tree,并且从来没有在下降的过程中分裂过节点。

通常我会递归地插入,这样 node->insert(key,data) 可以 return 一个新的键插入到父项中。父节点在子节点上调用 insert,如果子节点拆分它,return 就是父节点的新密钥。如果父级拆分,那么它 return 是其父级的键,等等

我发现插入实现可以通过这种方式保持非常干净。