Лекция
Привет, Вы узнаете о том , что такое дерево, Разберем основные их виды и особенности использования. Еще будет много подробных примеров и описаний. Для того чтобы лучше понимать что такое дерево, дерево b+ , настоятельно рекомендую прочитать все из категории Структуры данных.
2-3 дерево (англ. 2-3 tree) — структура данных, представляющая собой сбалансированное дерево поиска, такое что из каждого узла может выходить две или три ветви и глубина всех листьев одинакова. Является частным случаем B+ дерева.

2-3 дерево — сбалансированное дерево поиска, обладающее следующими свойствами:
Узел имеющий 2 сына называется двухместным. Узел имеющий три сына называется трехместным.
Высота 2-3 дерева, содержащего n узлов, не может быть больше [log(n+1) - 1] (целая часть).
2-3 дерево поиска.
Ключи размещаются в соответствии со следующими правилами:
1 Двухместный внутренний узел имеет двух сыновей. Ключ двухместного узла должен быть больше, чем ключи левого поддерева и меньше, чем ключи правого поддерева.
2 Трехместный узел имеет трех сыновей. Должен содержать два элемента данных. Ключ S больше TL, но меньше TM, а ключ L больше, чем TM, но меньше TR.

3 Лист может содержать один или два элемента данных.

Введем следующие обозначения:
Каждый узел дерева обладает полями:
Изначально t=root. Будем просматривать ключи в узлах, пока узел не является листом. Рассмотрим два случая:
T search(T x):
Node t = root
while (t не является листом)
if (t.length == 2)
if (t.keys < x)
t = t.sons
else
t = t.sons
else if (t.keys < x)
t = t.sons
else if (t.keys < x)
t = t.sons
else
t = t.sons
return t.keys

Поиск элемента 19, оранжевые стрелки обозначают путь по дереву при поиске
Если корня не существует — дерево пустое, то новый элемент и будет корнем (одновременно и листом). Иначе поступим следующим образом:
Найдем сперва, где бы находился элемент, применив search(x) Далее проверим есть ли у этого узла родитель, если его нет, то в дереве всего один элемент — лист. Возьмем этот лист и новый узел, и создадим для них родителя, лист и новый узел расположим в порядке возрастания.
Если родитель существует, то подвесим к нему еще одного сына. Если сыновей стало 4, то разделим родителя на два узла, и повторим разделение теперь для его родителя, ведь у него тоже могло быть уже 3 сына, а мы разделили и у него стало на 1 сына больше. (перед разделением обновим ключи).
function splitParent(Node t):
if (t.length > 3)
Node a = Node(sons = {t.sons , t.sons }, keys = {t.keys }, parent = t.parent, length = 2)
t.sons .parent = a
t.sons .parent = a
t.length = 2
t.sons = null
t.sons = null
if (t.parent != null)
t.parent[t.length] = a
t.length++
сортируем сыновей у t.parent
splitParent(t.parent)
else // мы расщепили корень, надо подвесить его к общему родителю, который будет новым корнем
Node t = root
root.sons = t
root.sons = a
t.parent = root
a.parent = root
root.length = 2
сортируем сыновей у root
Если сыновей стало 33, то ничего не делаем. Далее необходимо восстановить ключи на пути от новой вершины до корня:
function updateKeys(Node t):
Node a = t.parent
while (a != null)
for i = 0 .. a.length - 1
a.keys[i] = max(a.sons[i]) // max — возвращает максимальное значение в поддереве.
a = a.parent // Примечание: max легко находить, если хранить максимум
// правого поддерева в каждом узле — это значение и будет max(a.sons[i])
updateKeysupdateKeys необходимо запускать от нового узла. Добавление элемента:
function insert(T x):
Node n = Node(x)
if (root == null)
root = n
return
Node a = searchNode(x)
if (a.parent == null)
Node t = root
root.sons = t
root.sons = n
t.parent = root
n.parent = root
root.length = 2
сортируем сыновей у root
else
Node p = a.parent
p.sons[p.length] = n
p.length++
n.parent = p
сортируем сыновей у p
updateKeys(n)
split(n)
updateKeys(n)
Так как мы спускаемся один раз, и поднимаемся вверх при расщеплении родителей не более одного раза, то insertinsert работает за O(logn)O(logn).
Примеры добавления:

Добавление элемента с ключом 6
Пусть изначально t=searchNode(x) — узел, где находится xx.
Если у tt не существует родителя, то это корень (одновременно и единственный элемент в дереве). Удалим его.
Если pp существует, и у него строго больше 2 сыновей, то просто удалим t, а у p уменьшим количество детей.
Если у родителя tt два сына, рассмотрим возможные случаи (сперва везде удаляем t):
Обобщим алгоритм при удалении когда у родителя tt два сына:
В результате мы получаем корректное по структуре 2-3 дерево, но у нас есть нарушение в ключах в узлах, исправим их с помощью updateKeys() , запустившись от b.

Удаление элемента с ключом 2
В силу того, что наши узлы отсортированы по максимуму в поддереве, то следующий объект — это соседний лист справа. Попасть туда можно следующим образом: будем подниматься вверх, пока у нас не появится первой возможности свернуть направо вниз. Как только мы свернули направо вниз, будем идти всегда влево. Таким образом, мы окажемся в соседнем листе. Если мы не смогли ни разу свернуть направо вниз, и пришли в корень, то следующего объекта не существует. Случай с предыдущим симметричен.
T next(T x):
Node t = searchNode(x)
if (t.keys > x) //x не было в дереве, и мы нашли следующий сразу
return t.keys
while (t != null)
t = t.parent
if (можно свернуть направо вниз)
в t помещаем вершину, в которую свернули
while (пока t — не лист)
t = t.sons
return t
return t.keys

Путь при поиске следующего элемента после 2
B+ деревья, поддерживают операцию findfind, которая позволяет находить m следующих элементов. Наивная реализация выглядит следующим образом: будем вызывать mm раз поиск следующего элемента, такое решение работает за O(mlogn) . Но 2-3 деревья, позволяют находить m следующих элементов за O(m+logn) , что значительно ускоряет поиск при больших m. По построению, все листья у нас отсортированы в порядке возрастания, воспользуемся этим для нахождения m элементов. Нам необходимо связать листья, для этого модифицируем insert и delete Добавим к узлам следующие поля:
Пусть tt — добавленный узел. Изменим insert следующим образом: в самом конце, после того как мы уже обновили все ключи, найдем next(t) и запишем ссылку на него в t.rightt.right. Аналогично с левым.
Пусть t — удаляемый узел. Изменим delete следующим образом: в самом начале, до удаления tt, найдем следующий nextnext и запишем в next.left правый лист относительно t. С левым поступим аналогично.
В итоге, мы имеем двусвязный список в листьях, и чтобы нам вывести mm элементов, нам достаточно один раз найти нужный элемент и пробежаться вправо на mm элементов.

Анализ данных, представленных в статье про дерево, подтверждает эффективность применения современных технологий для обеспечения инновационного развития и улучшения качества жизни в различных сферах. Надеюсь, что теперь ты понял что такое дерево, дерево b+ и для чего все это нужно, а если не понял, или есть замечания, то не стесняйся, пиши или спрашивай в комментариях, с удовольствием отвечу. Для того чтобы глубже понять настоятельно рекомендую изучить всю информацию из категории Структуры данных
Комментарии
Оставить комментарий
Структуры данных
Термины: Структуры данных