拆分b +树中的节点

时间:2011-06-11 07:48:48

标签: tree b-tree multiway-tree

我正在试图弄清楚当节点溢出时到底发生了什么。 信息: 在我的b +树中,每个块有4个指针和3个数据部分。 问题: 我明白当有溢出时,我会分成两个节点,每个节点都有2个节点 键, 并插入父节点的中间值,而不从儿子中删除(与b树不同)。

然而我遇到了情况:

                                |21|30|50|

           |10|20|-|      |21|22|25|  |30|40|-| |50|60|80|  

我要插入密钥23 首先我分裂| 21 | 22 | 25 |成:| 21 | 22 | - |和| 23 | 25 | - | 现在我需要将键23插入父| 21 | 30 | 50 |巫婆导致另一个分裂。 | 21 | 23 | - |和| 30 | 50 | - | 但是指向30之前的指针在哪里? 是否有可能这个指针和& 23点后的那个| | | 23 | 25 | - | ?

5 个答案:

答案 0 :(得分:2)

插入23时:

  • 如你所说,21 | 22 | - |和| 23 | 25 | - |已创建
  • 2个节点需要父级
  • 在根节点中创建父级:| 21 | 23 | 30 | 50 |
  • 根目前有太多元素
  • 将根分为2个节点| 21 | 23 | - 和| 30 | 50 | -
  • 为2个新节点添加新父节点(恰好是树的新根节点)

基本上,该插入将使树的深度增加1

答案 1 :(得分:0)

以下是如何处理指针。这是插入之前的B +树。 (一些用于使指针更容易看到的填充)

                [21             30           50]
               /   \              \            \
       [10|20|-] => [21|22|25] => [30|40|-] => [50|60|80]  

插入23后,您将拥有2个节点。重要的是要知道左侧拆分应始终为同一实例,并且右侧拆分应为新节点。这将使处理指针更容易一些。

所以分裂应该是这样的。

  old          fresh 
[21|22|-] => [23|25|-]

由于左节点是同一个实例,因此root的键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被拆分,我们必须将中间键添加到左侧或右侧分割,并找到要升级的新中键。注意右侧分割时的空指针。因为该节点是新鲜的,所以必须尽快初始化!

root从右边拆分,意味着如果项目数量很少,则在右边节点中放置的项目较少。但一般来说,拆分的方式并不重要。)< / p>

我们的中键是23.所以我们加23。

                [21            23             30]          [ 50 ]
               /   \             \              \          *    \
       [10|20|-] => [21|22|-] => [23|25|-] => [30|40|-] => [50|60|80]  

好!如果这里没有拆分,我们的插入现在就完成了。但由于在这个层面存在分裂,我们必须找到新的中间关键来促进。也不要忘记新节点的空指针。

如果分割叶子,你必须在叶子上保留键值并提升中键的副本,但是如果分割内部节点,你可以安全地移动和提升键。)< / p>

此处新中键为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个数组。这样,键和节点指针总是保持在一起。最左边的子节点由一个单独的左指针处理,这个指针保存在每个内部节点上,并且永远不为空(在完成操作之后)。

答案 2 :(得分:0)

在B +树中,叶子和非叶子节点具有不同的结构。因此,它们的溢出机制也不同。您说的对叶节点溢出是正确的(无需删除子节点的父密钥即可完成)。但是,当非叶子节点溢出并被拆分时,子节点将不保留父密钥(父子密钥将从子子擦除)。

答案 3 :(得分:-1)

您必须了解发生的问题是因为您使用的表示模型不正确。您应该有一个与指针关联的数据值,以及数据值是指针引用的子树中最小值的不变量。

所以这就是你应该如何在插入前表示b树节点。

                         10|21|30|50|                       <--- root node

         10|20|    21|22|25|     30|40|       50|60|80|     <--- leaf nodes

在此表示形式中,根节点中值10之后的指针指向具有第一个值10等的叶节点。

当您插入23时,它将插入到叶节点中,其第一个值为21.这将生成以下树,无需拆分。

                         10|21|30|50|                       <--- root node

         10|20|    21|22|23|25|     30|40|    50|60|80|     <--- leaf nodes

当插入产生你所描述的效果的24时,你得到的是以下

                              10|30|                         <--- new root node

                        10|21|24|   30|50|                   <--- intermediate nodes

         10|20|   21|22|23|   24|25|   30|40|    50|60|80|   <--- leaf nodes

如你所见,不再存在歧义。叶节点被分割,并且键指针对24 |必须插入根节点。因为它已满,所以必须分开。现在有5个值,您将获得一个具有3个值的节点和一个具有2个节点的节点。您可以自由选择左侧节点还是右侧节点获取三个值。重要的是保留了基本不变量。节点中的每个键值是其关联指针引用的子树中的最小元素。必须添加一个新的根节点,其键值指针集现在应该是显而易见的。没有歧义。

这也很明显,许多优化策略都是可能的。我们可以将值21移动到未满的左叶节点,而不是拆分根节点。具有第一个值10的那个。这避免了分割根节点并保持b树高度不变并且产生更好的b树填充的需要。所以你最小化空间和搜索时间。但这意味着可以有效地检查横向平衡是否可行。仍然需要更改根节点中的键值。等

如您所见,b-tree中的值10在任何叶子节点中都不是真正需要的,并且在b-tree表示中经常被省略(即wikipedia)但这可能会让人感到困惑并且很可能你在哪里的原因。 :)

答案 4 :(得分:-1)

从我所教过的,最后一个节点可以有一个小于n / 2的节点。因此,在您的示例中,顶部节点将变为:

             |23|
         /        \
    |21|22|-|       |25|-|-|

我认为这有点道理。如果你考虑一下,你有5个离开节点,所以你需要从上层开始有5个指针。这种安排是唯一的方法,你可以有5个指针,所有其他组合将溢出节点或创建额外的指针。

如果节点是| 21 | 22 | - |和| 23 | 25 | - |,然后根节点将是| 23 | - | - |。那么,在右边节点中有23个是没有意义的,因为正确节点中的任何东西都必须等于或大于23!