689
правок
Изменения
Начал делать статью, основа - копипаста из Википедии.
'''АВЛ-дерево''' — сбалансированное по высоте двоичное дерево поиска: для каждой его вершины высота её двух поддеревьев различается не более чем на 1.
АВЛ-деревья названы по первым буквам фамилий их изобретателей, Г. М. Адельсона-Вельского и Е. М. Ландиса, которые впервые предложили использовать АВЛ-деревья в 1962.
== Балансировка ==
Относительно АВЛ-дерева балансировкой вершины называется операция, которая в случае разницы высот левого и правого поддеревьев = 2, изменяет связи предок-потомок в поддереве данной вершины так, что разница становится <= 1, иначе ничего не меняет. Указанный результат получается вращениями поддерева данной вершины.
Используются 4 типа вращений:
1.'''Малое левое вращение'''
[[Файл:AVL LR.GIF]]
Данное вращение используется тогда, когда (высота b-поддерева - высота L) = 2 и высота С <= высота R.
2.'''Большое левое вращение'''
[[Файл:AVL BR.GIF]]
Данное вращение используется тогда, когда (высота b-поддерева - высота L) = 2 и высота c-поддерева > высота R.
3.'''Малое правое вращение'''
[[Файл:AVL LL.GIF]]
Данное вращение используется тогда, когда (высота b-поддерева — высота R) = 2 и высота С <= высота L.
4.'''Большое правое вращение'''
[[Файл:AVL BL.GIF]]
Данное вращение используется тогда, когда (высота b-поддерева - высота R) = 2 и высота c-поддерева > высота L.
В каждом случае достаточно просто доказать то, что операция приводит к нужному результату и что полная высота уменьшается не более чем на 1 и не может увеличиться.
Из-за условия балансированности высота дерева О(lg(N)), где N- количество вершин, поэтому добавление элемента требует O(lg(N)) операций.
== Алгоритм добавления вершины ==
Показатель сбалансированности в дальнейшем будем интерпретировать как разность между высотой левого и правого поддерева, а алгоритм будет основаться на типе TAVLTree, описанном выше. Непосредственно при вставке (листу) присваивается нулевой баланс. Процесс включения вершины состоит из трех частей:
# Прохода по пути поиска, пока не убедимся, что ключа в дереве нет.
# Включения новой вершины в дерево и определения результирующих показателей балансировки.
# "Отступления" назад по пути поиска и проверки в каждой вершине показателя сбалансированности. Если необходимо - балансировка.
Расширим список параметров обычной процедуры вставки параметром-переменной flag, означающим, что высота дерева увеличилась.
Предположим, что процесс из левой ветви возвращается к родителю (рекурсия идет назад), тогда возможны три случая:
{ h<sub>l</sub> - высота левого поддерева, h<sub>r</sub> - высота правого поддерева }
Включение вершины в левое поддерево приведет к
# h<sub>l</sub> < h<sub>r</sub>: выравняется h<sub>l</sub> = h<sub>r</sub>. Ничего делать не нужно.
# h<sub>l</sub> = h<sub>r</sub>: теперь левое поддерево будет больше на единицу, но балансировка пока не требуется.
# h<sub>l</sub> > h<sub>r</sub>: теперь h<sub>l</sub> - h<sub>r</sub> = 2, - требуется балансировка.
В третьей ситуации требуется определить балансировку левого поддерева. Если левое поддерево этой вершины выше правого, то требуется двойной правый поворот, иначе хватит и малого.
Аналогичные (симметричные) рассуждения можно привести и для включение в правое поддерево.
== Алгоритм удаления вершины ==
Для простоты опишем рекурсивный алгоритм удаления.
Если вершина - лист, то удалим её и вызовем балансировку всех её предков в порядке от родителя к корню.
Иначе найдём самую близкую по значению вершину в поддереве наибольшей высоты (правом или левом) и переместим её на место удаляемой вершины, при этом вызвав процедуру её удаления.
Докажем, что данный алгоритм сохраняет балансировку. Для этого докажем по индукции по высоте дерева, что после удаления некоторой вершины из дерева и последующей балансировки высота дерева уменьшается не более, чем на 1. База индукции: Для листа очевидно верно. Шаг индукции: Либо условие балансированности в корне (после удаления корень может изменится) не нарушилось, тогда высота данного дерева не изменилась, либо уменьшилось строго меньшее из поддеревьев => высота до балансировки не изменилась => после уменьшится не более чем на 1.
Очевидно, в результате указанных действий процедура удаления вызывается не более 3 раз, так как у вершины, удаляемой по 2-му вызову, нет одного из поддеревьев. Но поиск ближайшего каждый раз требует O(N) операций, отсюда видна очевидная оптимизация: поиск ближайшей вершины производится по краю поддерева. Отсюда количество действий O(log(N)).
АВЛ-деревья названы по первым буквам фамилий их изобретателей, Г. М. Адельсона-Вельского и Е. М. Ландиса, которые впервые предложили использовать АВЛ-деревья в 1962.
== Балансировка ==
Относительно АВЛ-дерева балансировкой вершины называется операция, которая в случае разницы высот левого и правого поддеревьев = 2, изменяет связи предок-потомок в поддереве данной вершины так, что разница становится <= 1, иначе ничего не меняет. Указанный результат получается вращениями поддерева данной вершины.
Используются 4 типа вращений:
1.'''Малое левое вращение'''
[[Файл:AVL LR.GIF]]
Данное вращение используется тогда, когда (высота b-поддерева - высота L) = 2 и высота С <= высота R.
2.'''Большое левое вращение'''
[[Файл:AVL BR.GIF]]
Данное вращение используется тогда, когда (высота b-поддерева - высота L) = 2 и высота c-поддерева > высота R.
3.'''Малое правое вращение'''
[[Файл:AVL LL.GIF]]
Данное вращение используется тогда, когда (высота b-поддерева — высота R) = 2 и высота С <= высота L.
4.'''Большое правое вращение'''
[[Файл:AVL BL.GIF]]
Данное вращение используется тогда, когда (высота b-поддерева - высота R) = 2 и высота c-поддерева > высота L.
В каждом случае достаточно просто доказать то, что операция приводит к нужному результату и что полная высота уменьшается не более чем на 1 и не может увеличиться.
Из-за условия балансированности высота дерева О(lg(N)), где N- количество вершин, поэтому добавление элемента требует O(lg(N)) операций.
== Алгоритм добавления вершины ==
Показатель сбалансированности в дальнейшем будем интерпретировать как разность между высотой левого и правого поддерева, а алгоритм будет основаться на типе TAVLTree, описанном выше. Непосредственно при вставке (листу) присваивается нулевой баланс. Процесс включения вершины состоит из трех частей:
# Прохода по пути поиска, пока не убедимся, что ключа в дереве нет.
# Включения новой вершины в дерево и определения результирующих показателей балансировки.
# "Отступления" назад по пути поиска и проверки в каждой вершине показателя сбалансированности. Если необходимо - балансировка.
Расширим список параметров обычной процедуры вставки параметром-переменной flag, означающим, что высота дерева увеличилась.
Предположим, что процесс из левой ветви возвращается к родителю (рекурсия идет назад), тогда возможны три случая:
{ h<sub>l</sub> - высота левого поддерева, h<sub>r</sub> - высота правого поддерева }
Включение вершины в левое поддерево приведет к
# h<sub>l</sub> < h<sub>r</sub>: выравняется h<sub>l</sub> = h<sub>r</sub>. Ничего делать не нужно.
# h<sub>l</sub> = h<sub>r</sub>: теперь левое поддерево будет больше на единицу, но балансировка пока не требуется.
# h<sub>l</sub> > h<sub>r</sub>: теперь h<sub>l</sub> - h<sub>r</sub> = 2, - требуется балансировка.
В третьей ситуации требуется определить балансировку левого поддерева. Если левое поддерево этой вершины выше правого, то требуется двойной правый поворот, иначе хватит и малого.
Аналогичные (симметричные) рассуждения можно привести и для включение в правое поддерево.
== Алгоритм удаления вершины ==
Для простоты опишем рекурсивный алгоритм удаления.
Если вершина - лист, то удалим её и вызовем балансировку всех её предков в порядке от родителя к корню.
Иначе найдём самую близкую по значению вершину в поддереве наибольшей высоты (правом или левом) и переместим её на место удаляемой вершины, при этом вызвав процедуру её удаления.
Докажем, что данный алгоритм сохраняет балансировку. Для этого докажем по индукции по высоте дерева, что после удаления некоторой вершины из дерева и последующей балансировки высота дерева уменьшается не более, чем на 1. База индукции: Для листа очевидно верно. Шаг индукции: Либо условие балансированности в корне (после удаления корень может изменится) не нарушилось, тогда высота данного дерева не изменилась, либо уменьшилось строго меньшее из поддеревьев => высота до балансировки не изменилась => после уменьшится не более чем на 1.
Очевидно, в результате указанных действий процедура удаления вызывается не более 3 раз, так как у вершины, удаляемой по 2-му вызову, нет одного из поддеревьев. Но поиск ближайшего каждый раз требует O(N) операций, отсюда видна очевидная оптимизация: поиск ближайшей вершины производится по краю поддерева. Отсюда количество действий O(log(N)).