Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Algoritmy_-_Ekzamen.docx
Скачиваний:
0
Добавлен:
01.03.2025
Размер:
2.13 Mб
Скачать

10. Двоичные деревья поиска, операции добавления элементов.

Двоичные деревья поиска ( binary search tree, BST)

В двоичном дереве поиска каждая вершина может иметь (или не иметь) левого и правого ребенка, каждая вершина, кроме корня имеет родителя. При представлении с использованием указателей мы храним для каждой вершины дерева, помимо значения ключа key также указатели left, right и р (поддеревья левое, правое и родитель). Если ребенка (или родителя – для корня) нет, соответствующее поле содержит NULL.

Ключи в двоичном дереве поиска хранятся с соблюдением свойства упорядоченности.

Пусть х произвольная вершина двоичного дерева поиска. Если вершина у находится в левом поддереве вершины х, то key[y] < key[x]. Если у находится в правом поддереве х, то key[y] >key[x].

typedef int T;

struct Node{

T inf;

Node* left;

Node* right;

Node(T w): inf(w), left(0), right(0) { }

~Node(){ }

};

Добавление

class BTree{

Node* root;

Node* _Add(Node *r, T s)

{

if(r == 0)

r = new Node(s);

else if(s < r->inf)

r->left = _Add(r->left, s);

else

r->right = _Add(r->right, s);

return r;

}

11. Двоичные деревья поиска, операции удаления элементов и поиска следующих

Двоичные деревья поиска ( binary search tree, BST)

В двоичном дереве поиска каждая вершина может иметь (или не иметь) левого и правого ребенка, каждая вершина, кроме корня имеет родителя. При представлении с использованием указателей мы храним для каждой вершины дерева, помимо значения ключа key также указатели left, right и р (поддеревья левое, правое и родитель). Если ребенка (или родителя – для корня) нет, соответствующее поле содержит NULL.

Ключи в двоичном дереве поиска хранятся с соблюдением свойства упорядоченности.

Пусть х произвольная вершина двоичного дерева поиска. Если вершина у находится в левом поддереве вершины х, то key[y] < key[x]. Если у находится в правом поддереве х, то key[y] >key[x].

typedef int T;

struct Node{

T inf;

Node* left;

Node* right;

Node(T w): inf(w), left(0), right(0) { }

~Node(){ }

};

Удаление элемента из дерева

Дерево Т с корнем n и ключом K.

удалить из дерева Т узел с ключом K (если такой есть).

Алгоритм:

Если дерево T пусто, остановиться;

Иначе сравнить K с ключом X корневого узла n.

Если K>X, рекурсивно удалить K из правого поддерева Т;

Если K<X, рекурсивно удалить K из левого поддерева Т;

Если K=X, то необходимо рассмотреть три случая.

Если обоих детей нет, то удаляем текущий узел и обнуляем ссылку на него у родительского узла;

Если одного из детей нет, то значения полей ребёнка m ставим вместо соответствующих значений корневого узла, затирая его старые значения, и освобождаем память, занимаемую узлом m;

Если оба ребёнка присутствуют, то найдём узел m, являющийся следующим за данным;

скопируем данные (кроме ссылок на дочерние элементы) из m в n;

рекурсивно удалим узел m.

Элемент следующий за данным

Дано: дерево Т и ключ х

Возвращаем указатель на следующий за х элемент или NULL, если элемент х последний в дереве.

Алгоритм:

Отдельно рассматривает два случая.

Если правое поддерево вершины х не пусто, то следующий за х элемент – минимальный элемент в этом поддереве.

Иначе, если правое поддерево вершины х пусто. Переходим от х вверх, пока не найдём вершину, являющуюся левым сыном своего родителя. Этот родитель (если он есть) и будет искомым элементом.

12. АВЛ-деревья, основные операции

АВЛ-дерево - сбалансированное по высоте двоичное дерево поиска: для каждой его вершины высота её двух поддеревьев различается не более чем на 1.

АВЛ-дерево - скорость работы.

высота h АВЛ-дерева с n ключами лежит в диапазоне

от log2(n + 1) до 1.44 log2(n + 2) − 0.328.

h(n)=O(log2n)

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

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

Найдем высоту дерева – H(n).

  1. Пусть N(h) - минимальное количество узлов для АВЛ-дерева высотой h.

  2. N(h) = 1 + N(h - 1) + N(h - 2) N(h) > Fh, где Fh - h-e число Фиббоначи.

  3. Известно, что тогда H(n) = O(log(n))

Tprepare(n) = O(n • log(n))

Tfind(n) = O (log (n))

Будем представлять узлы АВЛ-дерева следующей структурой:

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

{

int key;

unsigned char height;

node* left;

node* right;

node(int k) { key = k; left = right = 0; height = 1; }

};

Поле key хранит ключ узла, поле height — высоту поддерева с корнем в данном узле, поля left и right — указатели на левое и правое поддеревья. Простой конструктор создает новый узел (высоты 1) с заданным ключом k.

Узлы АВЛ-дерева хранят не высоту, а разницу высот правого и левого поддеревьев, которая может принимать только три значения -1, 0 и 1. Эта разница хранится в переменной, размер которой равен минимум одному байту. Вспомним, что высота h < 1.44 log2(n + 2), это значит, например, что при n=109 (один миллиард ключей) высота дерева не превысит величины h=44, которая с успехом помещается в тот же один байт памяти, что и balance factor. Таким образом, хранение высот с одной стороны не увеличивает объем памяти, отводимой под узлы дерева, а с другой стороны существенно упрощает реализацию некоторых операций.

  • Определим три вспомогательные функции, связанные с высотой.

  • unsigned char height(node* p) - является оберткой для поля height, она может работать и с нулевыми указателями (с пустыми деревьями) - возвращает высоту дерева int bfactor(node* p)- вычисляет balance factor заданного узла (и работает только с ненулевыми указателями)

  • void fixheight(node* p)- функция восстанавливает корректное значение поля height заданного узла (при условии, что значения этого поля в правом и левом дочерних узлах являются корректными):

  • все функции являются нерекурсивными, т.е. время их работы есть величина О(1).

Механизм поддержание сбалансированности АВЛ-дерева – изменение структуры дерева при добавлении и удалении элементов.

Повороты:

  • Левый

  • Правый

  • Большой левый

  • Большой правый

Вставка узлов

Вставка нового ключа в АВЛ-дерево выполняется так же, как это делается в простых деревьях поиска: спускаемся вниз по дереву, выбирая правое или левое направление движения в зависимости от результата сравнения ключа в текущем узле и вставляемого ключа.

Единственное отличие заключается в том, что при возвращении из рекурсии (т.е. после того, как ключ вставлен либо в правое, либо в левое поддерево) выполняется балансировка текущего узла. Возникающий при такой вставке дисбаланс в любом узле по пути движения не превышает двух, а значит применение вышеописанной функции балансировки является корректным.

Удаление узлов

Для удалении вершины из АВЛ – дерева за основу взят алгоритм, который обычно применяется и при удалении узлов из стандартного двоичного дерева поиска. Находим узел p с заданным ключом k, в правом поддереве находим узел min с наименьшим ключом и заменяем удаляемый узел p на найденный узел min.

При реализации возникает несколько вариантов. Прежде всего, если у найденный узел p не имеет правого поддерева, то по свойству АВЛ-дерева слева у этого узла может быть только один единственный дочерний узел (дерево высоты 1), либо узел p вообще лист. В обоих этих случаях надо просто удалить узел p и вернуть в качестве результата указатель на левый дочерний узел узла p.

Пусть теперь правое поддерево у p есть. Находим минимальный ключ в этом поддереве. По свойству двоичного дерева поиска этот ключ находится в конце левой ветки, начиная от корня дерева. Применяем рекурсивную функцию findmin.

функция removemin - удалением минимального элемента из заданного дерева. По свойству АВЛ-дерева у минимального элемента справа либо единственный узел, либо там пусто. В обоих случаях надо просто вернуть указатель на правый узел и по пути назад (при возвращении из рекурсии) выполнить балансировку.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]