Вы находитесь на странице: 1из 28

Православный Свято-Тихоновский гуманитарный университет

Факультет информатики и прикладной математики


КАФЕДРА ИНФОРМАТИКИ

Курсовой проект по курсу «Структуры и алгоритмы компьютерной


обработки данных»
«2-3-дерево»

Автор: Сухоставский Святослав Олегович


Группа: 02.1.04
Проверил: профессор кафедры информатики, д.т.н. Соловьев А.В.

Москва 2021
Оглавление

1 Постановка задачи проекта.............................................................................3

2 Описание алгоритма.........................................................................................3

2.1 Добавление элемента....................................................................................4

2.2 Поиск Элемента.............................................................................................5

2.3 Удаление элемента........................................................................................6

3 Описание тестовых примеров.........................................................................8

4 Результаты тестов.............................................................................................8

5 Руководство пользователя...............................................................................8

6 Руководство системного программиста.......................................................11

7 Заключение.....................................................................................................11

Список литературы............................................................................................12

Приложение.......................................................................................................13

2
1 Постановка задачи проекта

Цель — разработать программную реализацию 2-3-дерева для


организации эффективного поиска числовых данных.
Для достижения цели нужно решить следующие задачи:
 изучить алгоритм работы с 2-3-деревом,
 разработать структуру хранения дерева,
 выполнить программную реализацию,
 проверить корректность программной реализации на тестовых
примерах.
Данный алгоритм актуален для использования при создании словарей [1],
а также может быть использован при проектировании прочих структур данных,
реализующих эффективный поиск.

2 Описание алгоритма

2-3-дерево — структура данных, представляющая собой сбалансированное


дерево поиска, такое, что из каждого узла может выходить две или три ветви и
глубина всех листьев одинакова [2].

Основные свойства 2-3-дерева:


 нелистовые вершины (узлы) имеют либо 2, либо 3 сына;
 нелистовые вершины, хранят два значения. Первое значение содержит
максимум поддерева, корнем которого служит первый сын, второе значение
содержит максимум поддерева, корнем которого служит второй сын
текущего узла;
 сыновья текущего узла упорядочены по наибольшему значению которое
содержится в образованных ими поддеревьях;
 все листья лежат на одной глубине;
 высота 2-3-дерева O(log n), где n — количество элементов в дереве;
 данные хранятся только в листовых вершинах.

3
2.1 Добавление элемента

Вставка нового элемента Х в 2-3-дерево:

1. Если дерево пусто, при добавлении элемента Х в дерево, этот элемент


становится его корнем.
2. Если дерево не пусто, ищется место, где должен находиться Х, другими
словами ищется родитель (р).
3. Если в дереве один узел (лист), то родителя (р) не существует (NULL). В
этом случае для имеющегося узла (листа) и вводимого узла (листа) с
элементом Х создается родитель, при этом узлы (листы) располагаются в
порядке возрастания (Рис.1).

Рис.1. Если в дереве один узел.

4. Если родитель (р) существует, он может иметь двух или трех сыновей. Если
сыновей было 2, то новый узел подсоединяется к родителю (р) таким
образом, чтобы все сыновья расположились в порядке возрастания значений.
Обновляются ключи.
5. В случае, когда в узел – родитель (р) имеет 3-х сыновей, этот узел
разделяется на два узла – родителя. Вводимый узел с элементом Х и уже
имеющиеся три узла – сыны распределяются между новообразованными
узлами – родителями (Рис.2).

4
Рис.2. Если у родителя (р) уже есть 3 сына.

6. Проверяется количество сыновей у родителя родителя (р). Если до


разделения родителя (р) на два узла он имел двух братьев, то пункты 5-6
повторяются для родителя родителя (р), предварительно обновляются
ключи. В случае, когда родитель родителя (р) не существует (т.е.
родитель (р) является корнем) в дереве создается новый корень и к нему
подключаются два узла полученные при разделении старого корня
(родителя (р)).

2.2 Поиск Элемента

Пусть нужно найти элемент со значением Х.


1. Текущей вершиной берется корень дерева.
2. Значение Х сравнивается с ключами текущей вершины. Если Х меньше
либо равен первому ключу, то текущей вершиной становится левый сын
текущей вершины. Если Х меньше либо равен второму ключу, то
текущей вершиной становится второй сын текущей вершины. Если Х
больше второго ключа, текущей вершиной назначается третий сын
текущей вершины. В последнем случае, третьего сына может не
оказаться, а это значит, что в дереве нет узла со значением Х.
3. Пункты 1-2 повторяются, пока текущая вершина не окажется листом.
Если текущая вершина - лист, то данная вершина является искомым
элементом.

5
2.3 Удаление элемента

1. Осуществляется поиск элемента, который необходимо удалить.


2. Если узел (t) с искомым элементом найдется, то проверяется наличие
родителя (р) данного узла. Если родитель (р) отсутствует, это означает,
что узел (t) является корнем, а так же единственным элементом в дереве.
Узел (t) удаляется.
3. Если родитель (р) существует, то проверяется количество его детей.
Если детей оказалось трое, узел (t) удаляется.
4. Когда количество детей равно двум, возможны несколько вариантов.
4.1.Если родитель (р) узла (t) является корнем, узел (t) удаляется вместе с
родителем (р), а второй сын при этом становится корнем (Рис.3).

Рис.3. Ситуация когда родитель (р) – корень и у него 2 сына.

4.2. Если у узла (np), брата родителя (р), количество сыновей равняется


трем, узел (t) удаляется. К родителю (р) присоединяется один сын
узла (np) (Рис.4).

6
Рис.4. Пример случая описанного в пункте 4.2.

4.3. Если у узла (np) количество сыновей равняется двум, то проверяется


количество сыновей у родителя родителя (р). Если сыновей трое,
удаляется узел (t), удаляется родитель (р), а его второй сын
подключается к узлу (np) (Рис.5). Если же сыновей двое, удаляется
узел (t), а его брат подключается к узлу (np). Поскольку нарушается
строение дерева, удаляемым узлом становится родитель (р) и для него
выполняются пункты 2-4.

Рис.5 Пример первого случая описанного в пункте 4.3

7
3 Структура данных для хранения дерева

Хранение узла дерева описывает класс Node. Узел содержит в себе:


 информацию (целочисленное значение),
 указатель на родительский узел,
 указатели на сыновей (левый сын, средний сын, правый сын ),
 ключи (первый и второй),
 количество сыновей.
Класс Tree описывает 2-3-дерево составленное из выше описанных узлов.
Дерево хранит в себе:
 указатель на корневой узел
 количество всех узлов дерева
 количество всех листовых узлов дерева

4 Описание тестовых примеров

Работа алгоритма проверялась на деревьях размером 1000, 10000, 100000,


1000000 листовых узлов. Проверялось наличие элементов в дереве,
осуществлялись выборочные поиск и удаление элементов.
Корректность расположения ключей в узлах дерева проверялась на деревьях
размером до 20 листовых узлов.

5 Результаты тестов

На рисунке 6 приведен график зависимости времени создания дерева от


количества листовых узлов.

8
600000

500000

400000

300000

200000

100000

0
0 200000 400000 600000 800000 1000000 1200000

Рис.6. График зависимости времени создания дерева от количества листовых


узлов.

Разница затраченного времени при удалении одного элемента в деревьях


размером 1 000 и 1 000 000 листов не превысила 1 миллисекунды.

6 Руководство пользователя

После запуска программы пользователю будет предложено меню, в


котором будут перечислены основные возможности программы (Рис.7).

Рис.7. Главное меню.

Поочередно рассмотрим пункты меню.


1. При вводе команды menu на экран выводится главное меню программы.
2. Командой start пользователь заканчивает работу с текущим деревом и
переключается на новое, при этом ему будет предложено загрузить дерево
9
из файла. Стоит обратить внимание, что файл с деревом должен
располагаться в той же директории, что и программа. При вводе команды
start пользователю будут предложены варианты перехода к новому дереву
(Рис.8).

Рис.8. Результат выполнения команды start.

2.1.Командой continue пользователь заканчивает работу с текущим деревом,


не сохраняя его.
2.2.Командой save пользователь завершает работу в текущем дереве,
предварительно сохранив его в текстовый файл. Процесс сохранения
дерева в файл будет описан ниже в пункте 7.
2.3.Введя команду cancel пользователь отказывается переходить в новое
дерево и продолжает работу в текущем.
3. Команда add() позволяет добавить новый элемент в текущее дерево. Для
этого в скобочках необходимо указать числовое значение, допускаются
только целые числам (Рис.9).

Рис.9. Пример использования команды add().

4. Команда search() осуществляет поиск значения указанного в скобках в


текущем дереве ( Рис.10).

Рис.10. Пример использования команды search().

5. Командой delete() пользователь удаляет из дерева узел содержащий


10
значение равное тому, что указано в скобках команды. Если такого узла нет,
удаление не происходит ( Рис.11).

Рис.11. Пример использования команды delete().

6. При вводе команды print на экран выводятся узлы дерева. Первым


выводится корень. В листовых вершинах указывается значение узла, в не
листовых вершинах указываются левый и правый ключи узла (Рис.12).

Рис.12. Пример вывода дерева на экран с использованием команды print.

7. Сохранение дерева в текстовый файл осуществляется вводом команды save.


После ввода команды пользователю будет предложено ввести имя файла в
который дерево будет сохранено (Рис.13).

Рис.13. Результат выполнения команды save.

После ввода имени файла, дерево автоматически в него сохраняется.


8. Команда exit завершает работу программы.

11
7 Руководство системного программиста

Программа запускается в операционной системе Windows.


Для запуска программы необходимо открыть файл 2-3-Tree.exe.

8 Заключение

В результате выполнения курсовой работы было изучено 2-3-дерево и


алгоритм его создания, разработана структура хранения данных, создана
программная реализация 2-3-дерева поиска. Программа была протестирована,
подробное описание находится в разделах “3 Описание тестовых примеров” и
“4 Результаты тестов”.
Данный алгоритм может быть применен при создании словарей, а
программа полученная в результате выполнения курсовой работы может быть
использована в учебных целях, для лучшего освоения данного алгоритма
учащимися.

12
Список литературы

1. Структуры и алгоритмы компьютерной обработки данных /


А.В.Соловьев. – М.: Изд-во ПСТГУ, 2013. – 128 с. (учебно-методическое
пособие);
2. 2-3 дерево – Викиконспекты [Электронный ресурс]. Дата обновления:
14.04.2021. URL: https://neerc.ifmo.ru/wiki/index.php?title=2-3_дерево (дата
обращения: 20.05.2021).

13
Приложение

Файл tree.h

/**
\file tree.h
\brief Класс описывающий дерево
\author Сухоставский С.О.
\date 20.05.2021
*/

#ifndef TREE_H_INCLUDED
#define TREE_H_INCLUDED

#include <iostream>
#include <fstream>
#include <vector>

///подключаем файл с классом Node


#include "node.h"

using namespace std;

class Tree
{
public:
///конструктор
Tree()
{
root=NULL;
length=0;
leaf_count=0;
}

///деструктор
~Tree()
{
if(root!=NULL)
{
root=delete_all_children(root);
leaf_count=0;
}
}

///возвращает количество узлов в дереве


int getLength()
{
return length;
}

///возвращает количество листов в дереве


int getLeaf_count()
{
return leaf_count;
}

///возвращает true, если передаваемый узел является корнем. Иначе false


bool isRoot(Node* node)
{
if(node->getParent()==NULL)
{
return true;

14
}
else
{
return false;
}
}

///возвращает указатель на корень


Node* getRoot()
{
return root;
}

///заполняет передаваемый массив значениями листовых вершин


void getTreeData(vector<long>& data_array)
{
if(root!=NULL)
{
get_all_tree_data(data_array,root);
}
}

///добавляет новый узел в дерево


void add_leaf(long leaf_data)
{
///если дерево пусто
if(root==NULL)
{
///создаем корень
root=new Node(NULL);

root->setData(leaf_data);
root->setKey(leaf_data,0);
root->setKey(leaf_data,1);

leaf_count++;
length++;
}
///если дерево не пусто
else
{
///создаем новый узел
Node *new_node = new Node(NULL);

new_node->setData(leaf_data);
new_node->setKey(leaf_data,0);
new_node->setKey(leaf_data,1);

leaf_count++;
length++;

///получаем узел родителя к которому необходимо добавить новосозданый узел


Node* parent_node = find_where_add(root,leaf_data);

///если родителя нет,значит в дереве только один элемент


if(parent_node==NULL)
{
///создаем новый корень
Node* new_root=new Node(NULL);
length++;
if(root->getData()<new_node->getData())
{
try_add_LSon(new_root,root);
try_add_MSon(new_root,new_node);
}

15
else
{
try_add_LSon(new_root,new_node);
try_add_MSon(new_root,root);
}
root=new_root;
update_keys(root);
}
///когда родитель существует
else
{
///если добавить новый узел к родительскому не получилось
if(!try_add_to_node(parent_node,new_node))
{
///вызываем метод который добавляет узел при этом перестраивая дерево
fix_tree(parent_node,new_node);
}
///если элемент получилось добавить обновляем ключи
else
{
update_keys(parent_node);
}
}
}

///осуществляет поиск элемента по передаваемому значению. Возвращает true,


///если элемент найден, иначе false
Node* search(long search_data)
{
///если дерево не пусто
if(root!=NULL)
{
return find_data(root,search_data);
}
return NULL;
}

///осуществляет удаление узла по передаваемому значению. Возвращает true,


///если элемент найден и успешно удален, иначе false
bool delete_leaf(long leaf_data)
{
///если дерево не пусто
if(root!=NULL)
{
///находим удаляемый узел
Node* deleted_node=search(leaf_data);
///если узел найден
if(deleted_node!=NULL)
{
///вызывается метод осуществляющий удаление узла с возможным перестроением дерева
smart_delete(deleted_node);
return true;
}
///если узла в дереве нет
else
{
return false;
}
}
///если дерево пусто
else
{
return false;

16
}

///осуществляет вывод всех узлов дерева в консоль


void printTree()
{
cout<<"\n"<<" ---------------------TREE---------------------"<<"\n";
///если дерево не пусто
if(root!=NULL)
{
print(root);
}
cout<<" ______________________________________________"<<"\n";
cout<<" Number of leaves: "<<leaf_count<<"\n";
cout<<" Tree size: "<<length<<"\n";
cout<<" ----------------------------------------------"<<"\n"<<"\n";
}

///осуществляет вывод всех узлов дерева в файл


void printTree(ofstream& output)
{
output<<" ---------------------TREE---------------------"<<"\n";
///если дерево не пусто
if(root!=NULL)
{
print(root,output);
}
output<<" ______________________________________________"<<"\n";
output<<" Number of leaves: "<<leaf_count<<"\n";
output<<" Tree size: "<<length<<"\n";
output<<" ----------------------------------------------"<<"\n"<<"\n";
}

private:
Node* root; ///указатель на корневой элемент
int length; ///каличество всех узлов дерева
int leaf_count;///каличество всех листовых вершин дерева

///рекурсивно удаляет передаваемый узел(если не обходимо перестраивает дерева)


void smart_delete(Node* deleted_node)
{
///создаем указатель на родителя удаляемого узла
Node* DeleteNodePerent=deleted_node->getParent();
///если удаляемый узел - это корень
if(isRoot(deleted_node))
{
delete deleted_node;
deleted_node=NULL;
leaf_count--;
length--;
root=NULL;
}
///если у родителя 3 сына
else if(DeleteNodePerent->getSonsCount()>2)
{
delete_node(deleted_node);
update_keys(DeleteNodePerent);
}
///если у родителя два сына
else
{
///если родитель-это корень
if(isRoot(DeleteNodePerent))
{

17
delete_node(deleted_node);

Node* old_root=root;

root=root->getLChild();
root->setParent(NULL);

delete old_root;
length--;
}
///когда родитель не корень
else
{
///создаем указатель на братский узел для родительского узла
Node* DeleteNodePerent_Brother=get_nearestBrother(DeleteNodePerent);

///если у брата родителя 3 сына


if(DeleteNodePerent_Brother->getSonsCount()>2)
{
delete_node(deleted_node);
///если братский узел левее родителья
if(DeleteNodePerent_Brother->getKey(1)<=DeleteNodePerent->getKey(0))
{
DeleteNodePerent->setMChild(DeleteNodePerent->getLChild());
DeleteNodePerent->setLChild(DeleteNodePerent_Brother->getRChild());
DeleteNodePerent_Brother->setRChild(NULL);
}
///если родитель левее своего брата
else
{
DeleteNodePerent->setMChild(DeleteNodePerent_Brother->getLChild());
DeleteNodePerent_Brother->setLChild(DeleteNodePerent_Brother->getMChild());
DeleteNodePerent_Brother->setMChild(DeleteNodePerent_Brother->getRChild());
DeleteNodePerent_Brother->setRChild(NULL);
}
///обновляем ключи
update_localNode_keys(DeleteNodePerent);
update_localNode_keys(DeleteNodePerent->getParent());
update_keys(DeleteNodePerent_Brother);
}
///если у брата родителя 2 сына
else
{
///если у родителя родителя удаляемого узла 3 сына
if(DeleteNodePerent->getParent()->getSonsCount()>2)
{
delete_node(deleted_node);
try_add_to_node(DeleteNodePerent_Brother,DeleteNodePerent->getLChild());
delete_node(DeleteNodePerent);
update_keys(DeleteNodePerent_Brother);
}
///если у родителя родителя удаляемого узла 2 сына
else
{
delete_node(deleted_node);
try_add_to_node(DeleteNodePerent_Brother,DeleteNodePerent->getLChild());
update_keys(DeleteNodePerent_Brother);
///рекурсивно вызываем данный метод для удаления родителя удаляемого узла
smart_delete(DeleteNodePerent);
}
}
}
}
}

18
///возвращает указатель на братский узел
Node* get_nearestBrother(Node* node)
{
///если текущий узел - это левый сын возвращаем среднего сына
if(node->getParent()->getLChild()==node)
{
return node->getParent()->getMChild();
}
///если текущий узел - это средний сын возвращаем левого сына
else if(node->getParent()->getMChild()==node)
{
return node->getParent()->getLChild();
}
///если текущий узел - это правый сын возвращаем среднего сына
else
{
return node->getParent()->getMChild();
}
}

///рекурсивно добавляет узел(если не обходимо перестроение дерева)


///parent_node - узел в который необходимо добавить сына
///new_node - сын, которого необходимо добавить
void fix_tree(Node* parent_node,Node* new_node)
{
///создаем новый пустой узел
Node* fix_node = new Node(NULL);
length++;

///разделяем сыновей родительского узла и новый узел между пустым узлом и родителем
split_sons(fix_node,parent_node,new_node);
///если родитель- это корень, создаем новый корень к нему подвешиваем родителя и пустой узел
if(isRoot(parent_node))
{
Node* new_root = new Node(NULL);
length++;

new_root->setLChild(fix_node);
fix_node->setParent(new_root);

new_root->setMChild(root);
root->setParent(new_root);
root=new_root;

update_keys(root);
}
///если родитель не корень
else
{
///если родитель родительского узла имеет 2 сына, подвешиваем пустой узел к нему
if(parent_node->getParent()->getSonsCount()<3)
{
///если пустой самый маленький, вешаем его левым сыном
if(fix_node->getKey(1)<=parent_node->getParent()->getKey(0))
{
try_add_LSon(parent_node->getParent(),fix_node);
}
///если он меньше среднего, вещаем его средним
else if(fix_node->getKey(1)<=parent_node->getParent()->getKey(1))
{
try_add_MSon(parent_node->getParent(),fix_node);
}
///если самый большой, вешаем правым
else
{

19
try_add_RSon(parent_node->getParent(),fix_node);
}

///обновляем ключи
update_keys(parent_node->getParent());
}
///если родитель родительского узла имеет 3 сына
else
{
///рекурсивно вызываем этот метод для добавления пустого узла к родителю родительского
fix_tree(parent_node->getParent(),fix_node);
}
}

///получает на вход пустой узел, родительский узел и новый узел(который добавляется в дерево)
///распределяет новый узел и детей родительского узла между родительским узлом и пустым узлом
///fix_node - пустой узел
///parent_node -родительский узел
///new_node - новый узел
void split_sons(Node* fix_node,Node* parent_node,Node* new_node)
{
///если новый узел самый маленький, он становится левым сыном пустого узла
if(new_node->getKey(1)<=parent_node->getKey(0))
{
fix_node->setLChild(new_node);
new_node->setParent(fix_node);

fix_node->setMChild(parent_node->getLChild());
parent_node->getLChild()->setParent(fix_node);

update_localNode_keys(fix_node);

parent_node->setLChild(parent_node->getMChild());
parent_node->setMChild(parent_node->getRChild());
delete_RSon(parent_node);

update_keys(parent_node);
}
///если новый узел меньше среднего сына родительского узла,
///он становится средним сыном пустого узла
else if(new_node->getKey(1)<=parent_node->getKey(1))
{
fix_node->setLChild(parent_node->getLChild());
parent_node->getLChild()->setParent(fix_node);

fix_node->setMChild(new_node);
new_node->setParent(fix_node);

update_localNode_keys(fix_node);

parent_node->setLChild(parent_node->getMChild());
parent_node->setMChild(parent_node->getRChild());
delete_RSon(parent_node);

update_keys(parent_node);
}
///если новый узел меньше правого сына родительского узла,
///он становится левым сыном родительского узла
else if(new_node->getKey(1)<=find_max_child(parent_node->getRChild()))
{
fix_node->setLChild(parent_node->getLChild());
parent_node->getLChild()->setParent(fix_node);

20
fix_node->setMChild(parent_node->getMChild());
parent_node->getMChild()->setParent(fix_node);

update_localNode_keys(fix_node);

parent_node->setLChild(new_node);
new_node->setParent(parent_node);

parent_node->setMChild(parent_node->getRChild());
delete_RSon(parent_node);

update_keys(parent_node);

}
///если новый узел больше правого сына родительского узла,
///он становится средним сыном родительского узла
else
{
fix_node->setLChild(parent_node->getLChild());
parent_node->getLChild()->setParent(fix_node);

fix_node->setMChild(parent_node->getMChild());
parent_node->getMChild()->setParent(fix_node);

update_localNode_keys(fix_node);

parent_node->setLChild(parent_node->getRChild());

parent_node->setMChild(new_node);
new_node->setParent(parent_node);
delete_RSon(parent_node);

update_keys(parent_node);

///рекурсивно выводит в консоль все узлы поддерева корень которого start_node


void print(Node* start_node)
{
start_node->printNode();

///если существует левый сын, печатаем его поддерево


if(start_node->getLChild()!=NULL)
{
print(start_node->getLChild());
}
///если существует средний сын, печатаем его поддерево
if(start_node->getMChild()!=NULL)
{
print(start_node->getMChild());
}
///если существует правый сын, печатаем его поддерево
if(start_node->getRChild()!=NULL)
{
print(start_node->getRChild());
}
}

///рекурсивно выводит в файл все узлы поддерева корень которого start_node


void print(Node* start_node,ofstream& output)
{
start_node->printNode(output);

21
///если существует левый сын, печатаем его поддерево
if(start_node->getLChild()!=NULL)
{
print(start_node->getLChild(),output);
}
///если существует средний сын, печатаем его поддерево
if(start_node->getMChild()!=NULL)
{
print(start_node->getMChild(),output);
}
///если существует правый сын, печатаем его поддерево
if(start_node->getRChild()!=NULL)
{
print(start_node->getRChild(),output);
}
}

///рекурсивно осуществляет поиск узла(начиная с start_node) со значением равным search_data


Node* find_data(Node* start_node,long search_data)
{
///если текущий узел это лист, проверяем его
if(start_node->isLeaf())
{
///если значение узла равно search_data, текущий узел- искомый
if(start_node->getData()==search_data)
{
return start_node;
}
///если не равно, искомый узел отсутствует в дереве
else
{
return NULL;
}
}
///если текущий узел не лист
else
{
///если искомое значение меньше первого ключа, ищем в левом сыне
if(search_data<=start_node->getKey(0))
{
return find_data(start_node->getLChild(),search_data);
}
///если искомое значение меньше второго ключа ищем в среднем сыне
else if(search_data<=start_node->getKey(1))
{
return find_data(start_node->getMChild(),search_data);
}
///если искомое значение больше второго ключа ищем в правом сыне
else
{
///если правый сын существует
if(start_node->getRChild()!=NULL)
{
return find_data(start_node->getRChild(),search_data);
}
///если не существует, искомое значение отсутствует в дереве
else
{
return NULL;
}
}
}
}

///рекурсивно ищет (начиная с start_node) родительский узел для значения(add_data)

22
Node* find_where_add(Node* start_node,long add_data)
{
///если текущий узел лист, возвращаем его родителя
if(start_node->isLeaf())
{
return start_node->getParent();
}
///если значение add_data меньше первого ключа ищем в левом сыне
if(add_data <= start_node->getKey(0))
{
start_node=start_node->getLChild();
return find_where_add (start_node,add_data);
}
///если значение add_data меньше второго ключа ищем в среднем сыне
if(add_data <= start_node->getKey(1))
{
start_node=start_node->getMChild();
return find_where_add (start_node,add_data);
}
///если значение add_data больше второго ключа ищем в правом сыне
else
{
///если правый сын не существует
if(start_node->getRChild()==NULL)
{
///если дети данного узла являются листьями, возвращаем этот узел
if(start_node->getLChild()->isLeaf())
{
return start_node;
}
///если дети не листья, ищем в среднем сыне
else
{
start_node=start_node->getMChild();
return find_where_add (start_node,add_data);
}
}
///если правый сын существует, ищем в нем
else
{
start_node=start_node->getRChild();
return find_where_add (start_node,add_data);
}
}

///удаляет переданый узел


///(обновляет: ключи и количество сыновей родителя, количество всех узлов и листовых вершин)
///!возможна не коректная работа для корня
void delete_node(Node* d_node)
{
///если текущий узел левый сын,его удаляем из родителя, остальных сдвигаем влево
if(d_node->getParent()->getLChild()==d_node)
{
d_node->getParent()->setLChild(d_node->getParent()->getMChild());
d_node->getParent()->setMChild(d_node->getParent()->getRChild());
d_node->getParent()->setRChild(NULL);
}
///если текущий узел средний сын, его удаляем из родителя, правого сына сдвигаем влево
else if(d_node->getParent()->getMChild()==d_node)
{
d_node->getParent()->setMChild(d_node->getParent()->getRChild());
d_node->getParent()->setRChild(NULL);
}

23
///если текущий узел правый сын, удаляем его у родителя
else
{
d_node->getParent()->setRChild(NULL);
}
update_localNode_keys(d_node->getParent());
///если удаляемый узел - лист, уменьшаем количество листовых вершин
if(d_node->isLeaf())
{
leaf_count--;
}
delete d_node;
d_node=NULL;
length--;

///возвращает true, если удалось добавить узел leaf к узлу node. Иначе false
bool try_add_to_node(Node* node, Node* leaf)
{
///если добавляемый меньше левого сына, пытаемся сделать его левым сыном
if(leaf->getKey(1)<=node->getKey(0))
{
return try_add_LSon(node,leaf);
}
///если добавляемый узел меньше среднего, пытаемся сделать его средним сыном
if(leaf->getKey(1)<=node->getKey(1))
{
return try_add_MSon(node,leaf);
}
///иначе, пытаемся сделать его правым сыном
else
{
return try_add_RSon(node,leaf);
}
}

///возвращает true, если удалось добавить узел son_node к узлу parent_node


///в качестве левого сына. Иначе false
bool try_add_LSon(Node* parent_node,Node* son_node)
{
///если левого сына нет, добавляем son_node
if(parent_node->getLChild()==NULL)
{
parent_node->setLChild(son_node);
son_node->setParent(parent_node);
return true;
}
///если количество детей меньше 3, сдвигаем узлы и добавляем son_node
else if(parent_node->getSonsCount()<3)
{
if(parent_node->getMChild()!=NULL)
{
parent_node->setRChild(parent_node->getMChild());
}
parent_node->setMChild(parent_node->getLChild());

parent_node->setLChild(son_node);
son_node->setParent(parent_node);
return true;

}
///если детей 3, операцию выполнить не возможно
else
{

24
return false;
}
}

///возвращает true, если удалось добавить узел son_node к узлу parent_node


///в качестве среднего сына. Иначе false
bool try_add_MSon(Node* parent_node,Node* son_node)
{
///если среднего сына нет, добавляем son_node
if(parent_node->getMChild()==NULL)
{
parent_node->setMChild(son_node);
son_node->setParent(parent_node);
return true;
}
///если правый сын не существует, сдвигаем средний узел и добавляем son_node
else if(parent_node->getRChild()==NULL)
{
parent_node->setRChild(parent_node->getMChild());
parent_node->setMChild(son_node);
son_node->setParent(parent_node);
return true;
}
///иначе, операцию выполнить не возможно
else
{
return false;
}
}

///возвращает true, если удалось добавить узел son_node к узлу parent_node


///в качестве правого сына. Иначе false
bool try_add_RSon(Node* parent_node,Node* son_node)
{
///если правого сына нет, добавляем son_node
if(parent_node->getRChild()==NULL)
{
parent_node->setRChild(son_node);
son_node->setParent(parent_node);
return true;
}
///иначе, операцию выполнить не возможно
else
{
return false;
}
}

///рекурсивно ищет максимальное значение в поддереве которое образует узел start_node


long find_max_child(Node* start_node)
{
///если текущий узел лист,возвращаем его значение
if(start_node->isLeaf())
{
return start_node->getData();
}
else
{
///если правый сын существует, ищем в нем
if(start_node->getRChild()!=NULL)
{
return find_max_child(start_node->getRChild());
}
///если средний сын существует, ищем в нем
else if(start_node->getMChild()!=NULL)

25
{
return find_max_child(start_node->getMChild());
}
///иначе, ищем в левом сыне
else
{
return find_max_child(start_node->getLChild());
}
}

///рекурсивно обновляет ключи в ветве узлов между parent_node и корнем


void update_branch_keys(Node* parent_node)
{
///если текущий узел не корень
if(!isRoot(parent_node))
{
///если текущий узел - левый сын,
///обновляем первый ключ его родителя и поднимаемся на уровень выше
if(parent_node == parent_node->getParent()->getLChild())
{
parent_node->getParent()->setKey(find_max_child(parent_node),0);
update_branch_keys(parent_node->getParent());
}
///если текущий узел - средний сын,
///обновляем второй ключ его родителя и поднимаемся на уровень выше
else if(parent_node == parent_node->getParent()->getMChild())
{
parent_node->getParent()->setKey(find_max_child(parent_node),1);
update_branch_keys(parent_node->getParent());
}
///если текущий узел - правый сын,поднимаемся на уровень выше
else if(parent_node->getParent()->getRChild()!=NULL)
{
update_branch_keys(parent_node->getParent());
}

}
}

///обновляет ключи локального узла(parent_node)


void update_localNode_keys(Node* parent_node)
{
///обновляем первый ключ
parent_node->setKey(find_max_child(parent_node->getLChild()),0);
///если существует средний сын, обновляем второй ключ
if(parent_node->getMChild()!=NULL)
{
parent_node->setKey(find_max_child(parent_node->getMChild()),1);
}
}

///обновляетключи локально а какже во всей ветке от локального узла и до корня


void update_keys(Node* parent_node)
{
update_localNode_keys(parent_node);
update_branch_keys(parent_node);
}

///рекурсивно заполняет входной массив значениями листовых узлов в поддереве узла start_node
void get_all_tree_data(vector<long>& data_array,Node* start_node)
{
///если существует левый сын, ищем значения в его поддереве
if(start_node->getLChild()!=NULL)

26
{
get_all_tree_data(data_array,start_node->getLChild());
}
///если существует средний сын, ищем значения в его поддереве
if(start_node->getMChild()!=NULL)
{
get_all_tree_data(data_array,start_node->getMChild());
}
///если существует правый сын, ищем значения в его поддереве
if(start_node->getRChild()!=NULL)
{
get_all_tree_data(data_array,start_node->getRChild());
}
///если узел - лист
else
{
///если есть данные, записываем их в массив
if(start_node->getData()!=-1)
{
data_array.push_back(start_node->getData());
}
}

///удаляет все поддерево образованое узлом node и сам узел


Node* delete_all_children(Node* node)
{
///если есть левый сын, удаляем его и его поддерево
if(node->getLChild()!= NULL)
{
node->setLChild(delete_all_children(node->getLChild()));
}
///если есть средний сын, удаляем его и его поддерево
if(node->getMChild()!= NULL)
{
node->setMChild(delete_all_children(node->getMChild()));
}
///если есть правый сын, удаляем его и его поддерево
if(node->getRChild()!= NULL)
{
node->setRChild(delete_all_children(node->getRChild()));
}
///если узел - лист, удаляем его
else
{
delete node;
length--;
node = NULL;
return node;
}
}

///удаляет левого сына


void delete_LSon(Node* parent_node)
{
parent_node->setLChild(NULL);
}

///удаляет среднего сына


void delete_MSon(Node* parent_node)
{
parent_node->setMChild(NULL);
}

27
///удаляет правого сына
void delete_RSon(Node* parent_node)
{
parent_node->setRChild(NULL);
}
};

#endif // TREE_H_INCLUDED

28

Вам также может понравиться