
- •1. Асимптотическая оценка алгоритма
- •2. Методика оценки не рекурсивных алгоритмов. Пример
- •3. Рекурсивные алгоритмы, построение асимптотической оценки. Пример
- •4. Методика оценки рекурсивных алгоритмов.
- •5. Алгоритмы сортировки массивов за полиномиальное время
- •6. Быстрые алгоритмы сортировки массивов, основанные на сравнении элементов
- •7. Алгоритмы сортировки массивов за линейное время
- •8. Линейные структуры данных, основные операции и характеристики
- •9. Деревья, виды, способы представления, структуры данных, обходы дерева
- •10. Двоичные деревья поиска, операции добавления элементов.
- •11. Двоичные деревья поиска, операции удаления элементов и поиска следующих
- •13. Двоичные кучи, основные операции и характеристики
- •16. Динамическое программирование, основные особенности
- •17. Жадные алгоритмы, основные особенности
- •18. Построение кода Хаффмена
- •19. Алгоритм обхода графов в ширину
- •20. Алгоритм обхода графов в глубину
- •21. Остовое дерево, классификация ребер, топологическая сортировка.
- •22. Сильно связные компоненты, алгоритм поиска
- •23. Построение минимально покрывающего дерева, алгоритм Крускала
- •24. Построение минимального покрывающего дерева, алгоритм Прима
- •25. Происк кратчайшего пути. Алгоритм Дейкстры
- •26. Поиск кратчайшего пути. Алгоритм Беллмана-Форда
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).
Пусть N(h) - минимальное количество узлов для АВЛ-дерева высотой h.
N(h) = 1 + N(h - 1) + N(h - 2) N(h) > Fh, где Fh - h-e число Фиббоначи.
Известно, что тогда 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 - удалением минимального элемента из заданного дерева. По свойству АВЛ-дерева у минимального элемента справа либо единственный узел, либо там пусто. В обоих случаях надо просто вернуть указатель на правый узел и по пути назад (при возвращении из рекурсии) выполнить балансировку.