以下是应该如何处理指针。这是插入之前的 B+ 树。(一些填充用于使指针更容易看到)
[21 30 50]
/ \ \ \
[10|20|-] => [21|22|25] => [30|40|-] => [50|60|80]
插入 23 后,您将拥有 2 个节点。重要的是要知道左拆分应该始终是同一个实例,而右拆分应该是一个新节点。这将使处理指针更容易一些。
所以分裂应该是这样的。
old fresh
[21|22|-] => [23|25|-]
由于左节点是同一个实例,21
根处的键具有正确的右指针。这是分裂根之前和分裂叶之后节点的样子。我们正处于过程的中间。
[21 30 50]
/ \ \ \
[10|20|-] => [21|22|-] => [23|25|-] => [30|40|-] => [50|60|80]
23
只应21
在根目录旁边添加一个带有密钥的新项目。由于 root 没有空间,它也会分裂。中间键是 23(右拆分的第一项)。
[21 30] [ 50 ]
/ \ \ * \
[10|20|-] => [21|22|-] => [23|25|-] => [30|40|-] => [50|60|80]
因为 root 是分裂的,所以我们必须将我们的中间键添加到左或右分裂,并找到一个新的中间键来提升。注意右拆分处的空指针。因为那个节点是新鲜的,它必须尽快初始化!
(根从正确的方向拆分,这意味着如果有奇数数量的项目,则较少的项目位于右节点中。但一般来说,你拆分的方式并不重要。)
我们的中间键是 23。所以让我们添加 23。
[21 23 30] [ 50 ]
/ \ \ \ * \
[10|20|-] => [21|22|-] => [23|25|-] => [30|40|-] => [50|60|80]
好的!如果这里没有拆分,我们的插入到此就完成了。但既然在这个层面有分裂,就必须找到新的中键来推动。也不要忘记新节点的空指针。
(在分裂叶子的情况下,您必须将键值保留在叶子上并提升中间键的副本,但在分裂内部节点的情况下,您可以安全地向上移动和提升键。)
这里新的中间键是 30。让我们从左节点弹出它。
30
\
[30|40|-]
(如何选择中间键很重要。总是从底部拆分获取中间键的节点,应该为新的中间键删除一个项目。如果该节点是左节点,则从中删除最后一项,否则删除第一项。)
[21 23] 30 [ 50 ]
/ \ \ \ * \
[10|20|-] => [21|22|-] => [23|25|-] => [30|40|-] => [50|60|80]
请注意,30 不在任何拆分内。这是我们必须处理的中间键。始终将中间键的右节点放在新节点的左侧。
[21 23] 30 [50]
/ \ \ * / \
[10|20|-] => [21|22|-] => [23|25|-] => [30|40|-] => [50|60|80]
然后中间键的右指针将指向右拆分的新节点。最后中间键被提升,新的根在左边分裂和右边分裂和中间键的键被创建。
[ 30 ]
/ \
[21 23] [50]
/ \ \ / \
[10|20|-] => [21|22|-] => [23|25|-] => [30|40|-] => [50|60|80]
恭喜!你已经完成了插入。它在纸上看起来很容易,但我不得不承认它在编码时有点挑战。
有几种方法可以key-node
在内存中表示这些项目。我的方法是每个key-node
项目都有一个带键的右指针。所以每个内部节点都有key-node
s数组。这样,键和节点指针总是保持在一起。最左边的孩子由一个单独的左指针处理,这个指针保存在每个内部节点上并且永远不会为空(在完成操作之后)。