разделение узла в дереве b+

Я пытаюсь выяснить, что именно происходит, когда происходит переполнение узла. информация: в моем дереве b+ есть 4 указателя на блок и 3 раздела данных. проблема: я понял, что при переполнении мы разбиваем на 2 узла, в моем случае каждый с 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|-| ?


person farchy    schedule 11.06.2011    source источник


Ответы (5)


При вставке 23:

  • как вы сказали, 21|22|-| и |23|25|-| созданы
  • 2 узла требуют родителя
  • родитель создается в корневом узле: |21|23|30|50|
  • в корне сейчас слишком много элементов
  • разделить корень на 2 узла |21|23|- и |30|50|-
  • добавьте нового родителя для 2 новых узлов (который является новым корнем дерева)

По сути, эта вставка увеличит глубину дерева на 1.

person Victor Hurdugaci    schedule 11.06.2011
comment
спасибо за быстрый ответ, но вопрос в том, что происходит с указателями, как уже упоминалось. может ли быть, что 2 указателя указывают на один и тот же узел? - person farchy; 11.06.2011
comment
кто-то ответил, что указатель справа от ключа 23 будет указывать на вновь созданный узел |30|50|-|. по какой-то причине он был удален, это верно для нелистового узла??? - person farchy; 11.06.2011

Вот как следует обращаться с указателями. это ваше дерево 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]  

Поскольку корень разделен, мы должны добавить наш средний ключ к левому или правому разделению и найти новый средний ключ для продвижения. обратите внимание на нулевой указатель справа. поскольку этот узел свежий, его нужно скоро инициализировать!

(корень был отделен от правого, что означает, что меньше элементов лежит в правом узле в случае нечетного количества элементов. но в целом не имеет значения, как вы разделяете.)

наш средний ключ был 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-nodes. таким образом, ключи и указатели узлов всегда хранятся вместе. левый самый дочерний элемент обрабатывается отдельным левым указателем, этот указатель хранится на каждом внутреннем узле и никогда не становится нулевым (после завершения операций).

person M.kazem Akhgary    schedule 09.01.2018

В дереве B+ листовые и нелистовые узлы имеют разную структуру. В связи с этим различаются и механизмы их перелива. То, что вы говорите, верно для переполнения листовых узлов (это делается без стирания родительского ключа из сына). Но когда нелистовые узлы переполняются и разделяются, дочерний узел не сохраняет родительский ключ (родительский ключ стирается из сына).

person shantanu4raje    schedule 15.10.2020

Проблема, которую вы должны понять, что происходит, связана с плохой моделью представления, которую вы используете. У вас должно быть одно значение данных, связанное с указателем, и инвариант, согласно которому значение данных является наименьшим значением в поддереве, на которое ссылается указатель.

Вот как вы должны представлять узел 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-дерева. Так вы минимизируете пространство и время поиска. Но это означает, что можно эффективно проверить, возможна ли боковая балансировка. Изменение значения ключа в корневом узле по-прежнему необходимо. и т. д.

Как видите, значение 10 в вашем b-дереве на самом деле не требуется ни для каких конечных узлов, и оно часто опускается в представлении b-дерева (т.е. wikipedia), но это может сбивать с толку и, вероятно, именно поэтому вы туда и попали. :)

person chmike    schedule 11.06.2011
comment
Я считаю, что в вашей модели есть ошибка. Первое представление имеет 4 значения, которые подразумевают, что от корня существует 5 дочерних узлов. Я думаю, что вы на самом деле рассматриваете значения, представленные как значения указателей, но автор исходного вопроса представил значения В узле. Итак, первая модель в этом вопросе имеет 3 значения в корневом узле и 4 непредставленных указателя на конечные узлы. - person Victor Hurdugaci; 11.06.2011
comment
Это не ошибка, потому что это b+дерево, а не b-дерево. В b+дереве каждая пара ключ/данные хранится в листе. В b-дереве пары ключ/данные хранятся в листах и ​​в узлах верхнего уровня. То, что показано в вопросе, представляет собой дерево b +, и это также то, что я показываю. Как я уже сказал, можно отбросить одну из пар ключ/данные, потому что для нахождения ключа знать не обязательно, но это работает только на некоторых узлах. Инвариант, который я описал, делает все очень простым и устраняет всякую двусмысленность. - person chmike; 13.06.2011

Из того, чему меня научили, последний узел может иметь на один узел меньше, чем n/2. Итак, в вашем примере верхние узлы станут:

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

Я думаю, это имеет смысл. Если подумать, у вас есть 5 выходных узлов, поэтому вам нужно 5 указателей с верхнего уровня. Такое расположение — единственный способ, при котором у вас может быть 5 указателей, все остальные комбинации либо переполнят узел, либо создадут дополнительные указатели.

Если узлы были |21|22|-| и |23|25|-|, то корневой узел будет |23|-|-|. Тогда не имеет смысла иметь 23 в правом узле, потому что все, что находится в правом узле, в любом случае должно быть больше или равно 23!

person kevin    schedule 01.05.2013