- •1. Вычислительные модели. Показатели эффективности алгоритмов, задача анализа алгоритмов. Время выполнения алгоритма для худшего и среднего случая. Вычислительные модели
- •Задача анализа алгоритмов
- •Время работы алгоритма
- •Время выполнения в худшем и среднем случае
- •5. Анализ рекурсивных алгоритмов. Анализ вставки элемента в бинарное дерево поиска.
- •5.Анализ сложности рекурсивных алгоритмов
- •6. Связные списки: однонаправленные, двунаправленные, кольцевые. Примеры реализации.
- •Однонаправленные (односвязные) списки
- •Двунаправленные (двусвязные) списки
- •7. Структура данных "стек". Реализация с помощью массива и связного списка, смешанная реализация. Пример использования (кроме задачи о скобках)
- •8.Структура данных "очередь". Реализация с помощью массива, циклического массива, односвязного списка.
- •Очереди
- •Бинарные деревья
- •11. Обходы бинарных деревьев (клп - "Корень-Левое-Правое" ,лкп,лпк) с примерами программной реализации.
- •12. Удаление элемента из бинарного дерева поиска.
- •13.Рандомизированные деревья. Вставка элемента в рандомизированное бинарное дерево поиска.
- •Включение элемента в рандомизированное дерево
- •Рекурсивный алгоритм вставки в рандомизированное дерево:
- •14.Быстрая сортировка Хоара (QuickSort)
- •Краткое описание алгоритма
- •Алгоритм
- •Оценка эффективности
- •15. Сортировка слиянием (MergeSort)
- •16.Полный перебор подмножеств
- •17. Полный перебор перестановок
- •18. Перебор с возвратом и отсечением вариантов
- •3.1 Использование рекурсии для записи алгоритма
- •3.2 Примеры решения задач при помощи перебора с возвратом
- •.1 Отсечение лишних вариантов
- •19. Динамическое программирование. Разбиение задачи на подзадачи, принцип оптимальности. Задача о маршруте из верхнего угла таблицы в нижний
- •20. Пример использования динамического программирования для подсчета количеств комбинаторных объектов - найти количество разбиений числа на слагаемые
- •21. Жадные алгоритмы. Принцип жадного выбора, свойство оптимальности для подзадач, схема доказательства корректности жадного алгоритма. Задача о заявках.
- •Принцип жадного выбора
- •Оптимальность для подзадач
- •Примеры Размен монет
- •Выбор заявок
- •22. Хеширование с цепочками. Эффективность хеширования с цепочками. Хеш-функции, качество хеш-функций, подходы к построению хеш-функций.
- •23. Хеширование с открытой адресацией. Способы разрешения коллизий. Анализ эффективности.
13.Рандомизированные деревья. Вставка элемента в рандомизированное бинарное дерево поиска.
В идеально
сбалансированном дереве трудоемкость поиска составляет О(log2 n) , а построение дерева по трудоемкости пропорционально О(n*log2 n). Но поддержание идеально сбалансированного дерева при включении новых элементов требует больших затрат, поэтому при балансировке используются более мягкие критерии. Если внешние узлы дерева расположены на одном, в крайнем случае, двух нижних уровнях, то обеспечивается достаточно высокая производительность.
Существует много BST-деревьев, высота которых ограничена величиной 2*log2 n.
Один из методов балансировки – построение рандомизированных BST-деревьев [4].
Включение элемента в рандомизированное дерево
При построении рандомизированного дерева основное исходное положение заключается в том, что любой элемент может с одинаковой вероятностью быть корнем дерева. Причём это справедливо и для поддеревьев рандомизированного дерева. Поэтому при включении нового элемента в дерево, случайным образом выбирается включение элемента в качестве листа, или в качестве корня. Вероятность появления нового узла в корне дерева или поддерева определяется, как 1/(n+1), где n – число узлов в дереве или в поддереве. То есть, дерево выглядит так, будто элементы поступали в него в случайном порядке. Поскольку принимаемые алгоритмом решения являются случайными, при каждом выполнении алгоритма при одной и то же последовательности включений будут получаться деревья различной конфигурации. Таким образом, несмотря на увеличение трудоёмкости операции включения, за счет рандомизации получается структура дерева, близкая к сбалансированной. Благодаря сбалансированности дерева трудоёмкость в рандомизированном дереве имеет оценку О(log2n) и в среднем операции более эффективны, чем в BST-дереве.
Рекурсивный алгоритм вставки в рандомизированное дерево:
RND_Insert(t, k, data, inserted)
1. t – корень дерева или поддерева
2. k –ключ нового элемента
3. data – данные нового элемента
4. inserted – возвращаемый признак вставки
5. if t = nil
6. then t ← Create_Node(k, data)
7. n[t] ← 1
8. inserted ← TRUE
9. return t
10. if Rand() < RAND_MAX/(n[t]+1)
11. then t ← Root_Insert(t, k, data, ins)
12. inserted ← ins
13. return t
14. if k = key[t]
15. then inserted ← FALSE
16. return t
17. if k < key[t]
18. then left[t] ← RND_Insert (left[t], k, data, ins)
19. else right[t] ← RND_Insert (right[t], k, data, ins)
20. inserted ← ins
21. if inserted = TRUE
22. then n[t] ← n[t] + 1
23. return t
Поскольку принимаемые алгоритмом решения являются случайными, то при каждом выполнении алгоритма для одной и той же последовательности включений будут получаться различные конфигурации рандомизированного дерева. Таким образом, не смотря на увеличение трудоемкости операции включения, за счет рандомизации получается структура дерева, близкая к сбалансированной.
Поиск в рандомизированном дереве ведется по стандартному алгоритму поиска в BST – дереве. Благодаря сбалансированности дерева в среднем поиск оценивается нотацией O(log2 n) и более эффективен, чем в BST – дереве.
Недостатком приведенного алгоритма являются затраты на генерацию случайных чисел в узлах, лежащих на пути операции включения. Чтобы минимизировать эти затраты, можно вместо системного генератора случайных чисел использовать числа, которые не являются совершенно случайными, но для генерации требуют небольших затрат. Например, функцию Rand() можно заменить проверкой условия 111 mod n[t] = 3 для принятия решения о вставке в корень [4].
Второй недостаток – необходимость поддержки параметра n[t] в узлах рандомизированного дерева. Но, если этот параметр необходим по другим причинам, например для решения задач о порядковых статистиках, то этот недостаток не является решающим.
Вставка элемента x во множество. Выполняется так:
Начинаем с корневого узла дерева (его адрес записан в root). Сравниваем x со значением v в этом узле. Если они равны, то мы нашли x и делать ничего не надо – выходим из процедуры. Если x меньше v, то переходим по левому указателю (left), если больше – по правому (right). Продолжаем делать так до тех пор, пока указатель, по которому надо переходить, не окажется NIL. В этом случае создаём новый узел и его адрес помещаем в этот указатель.
И не забываем ещё один нюанс. Если элемент вставляется в пустое дерево, то адрес созданного узла нужно записать в переменную root.
Приведём пример реализации:
procedure ins(var root: PNode; x: integer);
var
r: PNode;
begin
if root=NIL then begin
new(root);
root^.left:=NIL; root^.right:=NIL; root^.v:=x;
exit;
end;
r:=root;
repeat
if r^.v=x then exit;
if x<r^.v then
begin
if r^.left<>NIL then
r:=r^.left
else
begin
new(r^.left);
r:=r^.left;
r^.left:=NIL; r^.right:=NIL; r^.v:=x;
exit;
end
end
else
begin
if r^.right<>NIL then
r:=r^.right
else
begin
new(r^.right);
r:=r^.right;
r^.left:=NIL; r^.right:=NIL; r^.v:=x;
exit;
end;
end;
until false;
end;