Дерево AA - AA tree
Эта статья нужны дополнительные цитаты для проверка.Июнь 2011 г.) (Узнайте, как и когда удалить этот шаблон сообщения) ( |
An Дерево AA в Информатика это форма сбалансированное дерево используется для эффективного хранения и извлечения упорядоченных данных. Деревья AA названы в честь Арне Андерссон, их изобретатель.
Деревья AA представляют собой разновидность красно-черное дерево, форма двоичное дерево поиска который поддерживает эффективное добавление и удаление записей. В отличие от красно-черных деревьев, красные узлы в дереве AA можно добавить только как правый дочерний элемент. Другими словами, никакой красный узел не может быть левым дочерним элементом. Это приводит к моделированию 2-3 дерева вместо 2-3-4 дерево, что значительно упрощает обслуживание. Алгоритмы обслуживания красно-черного дерева должны учитывать семь различных форм, чтобы правильно сбалансировать дерево:
С другой стороны, дерево AA должно учитывать только две формы из-за строгого требования, что только правые ссылки могут быть красными:
Балансировка вращений
В то время как красно-черные деревья требуют одного бита балансирующих метаданных на узел (цвет), деревья AA требуют O (log (log (N))) бит метаданных на узел в форме целочисленного «уровня». Для AA-деревьев имеют место следующие инварианты:
- Уровень каждого листового узла равен единице.
- Уровень каждого левого потомка ровно на единицу меньше, чем у его родителя.
- Уровень каждого правого ребенка равен уровню его родителя или на единицу меньше.
- Уровень каждого правого внука строго ниже, чем у его прародителя.
- Каждый узел уровня выше одного имеет двух дочерних узлов.
Ссылка, в которой уровень ребенка равен уровню его родителя, называется горизонтальный ссылка, и аналогична красной ссылке в красно-черном дереве. Разрешены отдельные правые горизонтальные ссылки, но запрещены последовательные; все левые горизонтальные ссылки запрещены. Это более строгие ограничения, чем аналогичные ограничения для красно-черных деревьев, в результате чего повторная балансировка дерева AA процедурно намного проще, чем повторная балансировка красно-черного дерева.
Вставки и удаления могут временно привести к тому, что дерево AA станет несбалансированным (то есть нарушит инварианты дерева AA). Для восстановления баланса нужны всего две различные операции: «перекос» и «разбиение». Наклон - это поворот вправо для замены поддерева, содержащего левую горизонтальную ссылку, на поддерево, содержащее правую горизонтальную ссылку. Разделение - это поворот влево и повышение уровня для замены поддерева, содержащего две или более последовательных правых горизонтальных ссылки, одной, содержащей на две последовательных правых горизонтальных ссылки меньше. Реализация вставки и удаления с сохранением баланса упрощается за счет использования операций перекоса и разделения для изменения дерева только в случае необходимости, вместо того, чтобы заставлять их вызывающие стороны решать, перекосить или разделить.
функция перекос является Вход: T, узел, представляющий дерево AA, которое необходимо перебалансировать. выход: Еще один узел, представляющий ребалансированное дерево AA. если ноль (T) тогда возвращаться Ноль иначе если ноль (слева (T)) тогда возвращаться Т иначе если уровень (слева (T)) == уровень (T) тогда Поменяйте местами указатели горизонтальных левых ссылок. L = влево (T) влево (T): = вправо (L) вправо (L): = T возвращаться L еще возвращаться Т конец, есликонечная функция
Перекос:
функция расколоть является Вход: T, узел, представляющий дерево AA, которое необходимо перебалансировать. выход: Еще один узел, представляющий ребалансированное дерево AA. если ноль (T) тогда возвращаться Ноль иначе если ноль (справа (T)) или же ноль (справа (справа (T))) тогда возвращаться Т иначе если уровень (T) == уровень (справа (справа (T))) тогда У нас есть две горизонтальные правые ссылки. Возьмите средний узел, поднимите его и верните. R = правый (T) правый (T): = левый (R) левый (R): = T уровень (R): = уровень (R) + 1 возвращаться р еще возвращаться Т конец, есликонечная функция
Расколоть:
Вставка
Вставка начинается с обычной процедуры поиска и вставки в двоичное дерево. Затем, когда стек вызовов раскручивается (при условии рекурсивной реализации поиска), легко проверить правильность дерева и при необходимости выполнить любые вращения. Если возникает горизонтальная левая ссылка, выполняется перекос, а если возникают две горизонтальные правые связи, выполняется разделение, возможно, увеличивая уровень нового корневого узла текущего поддерева. Обратите внимание, что в приведенном выше коде приращение уровня (T). Это заставляет продолжать проверку достоверности дерева, поскольку модификации всплывают из листьев.
функция вставлять является Вход: X - значение, которое нужно вставить, и T - корень дерева, в которое его нужно вставить. выход: Сбалансированная версия T, включая X. Выполните обычную процедуру вставки двоичного дерева. Установите результат рекурсивный вызов правильного дочернего элемента в случае создания нового узла или корень поддерева изменяется. если ноль (T) тогда Создайте новый листовой узел с X. возвращаться узел (X, 1, Nil, Nil) иначе если X <значение (T) тогда left (T): = вставить (X, left (T)) иначе если X> значение (T) тогда right (T): = вставить (X, right (T)) конец, если Обратите внимание, что случай X == value (T) не указан. Как указано, вставка не будет иметь никакого эффекта. Разработчик может пожелать другого поведения. Выполните перекос, а затем разделите. Условные выражения, определяющие, будет ли ротация не произойдет или не будет внутри процедур, как указано над. T: = перекос (T) T: = раскол (T) вернуть Tконечная функция
Удаление
Как и в большинстве сбалансированных бинарных деревьев, удаление внутреннего узла может быть превращено в удаление листового узла путем замены внутреннего узла либо его ближайшим предшественником, либо его преемником, в зависимости от того, какие из них находятся в дереве, или от прихоти разработчика. Чтобы получить предшественника, достаточно перейти по одной левой ссылке, а затем по всем оставшимся правым ссылкам. Точно так же преемника можно найти, пройдя один раз вправо и влево, пока не будет найден нулевой указатель. Из-за свойства AA всех узлов уровня выше одного, имеющих двух дочерних узлов, узел-преемник или предшественник будет на уровне 1, что делает их удаление тривиальным.
Чтобы сбалансировать дерево, есть несколько подходов. Тот, что описал Андерссон в его оригинальная бумага - самый простой, и он описан здесь, хотя в реальных реализациях может быть выбран более оптимизированный подход. После удаления первым шагом к поддержанию достоверности дерева является понижение уровня всех узлов, чьи дочерние элементы находятся на два уровня ниже их, или у которых отсутствуют дочерние элементы. Затем весь уровень нужно перекосить и разделить. Этот подход получил поддержку, потому что в концептуальном плане он включает три легко понимаемых отдельных шага:
- При необходимости уменьшите уровень.
- Наклоните уровень.
- Разделите уровень.
Однако на этот раз мы должны перекосить и разделить весь уровень, а не только узел, что усложняет наш код.
функция Удалить является Вход: X - значение для удаления и T - корень дерева, из которого его следует удалить. выход: Т, сбалансированный, без значения X. если ноль (T) тогда вернуть T иначе если X> значение (T) тогда right (T): = delete (X, right (T)) иначе если X <значение (T) тогда left (T): = delete (X, left (T)) еще Если мы лист, то все просто, иначе сведем к листу. если лист (T) тогда вернуться вправо (T) иначе если ноль (слева (T)) тогда L: = преемник (T) справа (T): = удалить (значение (L), справа (T)) значение (T): = значение (L) еще L: = предшественник (T) влево (T): = удалить (значение (L), слева (T)) значение (T): = значение (L) конец, если конец, если Восстановите баланс дерева. Понизьте уровень всех узлов на этом уровне, если необходимо, а затем наклонить и разделить все узлы на новом уровне. T: = уменьшение_уровня (T) T: = наклон (T) вправо (T): = наклон (вправо (T)) если не nil (right (T)) right (right (T)): = skew (right (right (T))) конец, если T: = разделить (T) вправо (T): = разделить (вправо (T)) вернуть Tконечная функция
функция уменьшение_уровня является Вход: T, дерево, из которого мы хотим удалить ссылки, пропускающие уровни. выход: T с его уровнем снизился. should_be = min (уровень (слева (T)), уровень (справа (T))) + 1 если should_be <уровень (T) тогда level (T): = should_be если should_be <уровень (справа (T)) тогда уровень (справа (T)): = should_be конец, если конец, если вернуть Tконечная функция
Хороший пример удаления по этому алгоритму присутствует в Бумага Андерссона.
Спектакль
Производительность дерева AA эквивалентна производительности красно-черного дерева. В то время как дерево AA совершает больше вращений, чем красно-черное дерево, более простые алгоритмы, как правило, работают быстрее, и все это уравновешивает, чтобы привести к аналогичной производительности. Красно-черное дерево более стабильно по своим характеристикам, чем дерево AA, но дерево AA имеет тенденцию быть более плоским, что приводит к немного более быстрому времени поиска.[1]
Смотрите также
Рекомендации
- ^ "Исследование характеристик производительности структур данных дерева двоичного поиска (страницы 67-75)" (PDF). Архивировано из оригинал (PDF) на 2014-03-27. Получено 2010-10-17.
внешняя ссылка
- А. Андерссон. Сбалансированные деревья поиска стали проще
- А. Андерссон. Примечание о поиске в двоичном дереве поиска
- BSTlib - Древовидная библиотека AA с открытым исходным кодом для C от trijezdci
- AA Visual 2007 1.5 - программа на Delphi с открытым исходным кодом для обучения древовидной структуре AA
- Тщательное руководство Жюльен Уокер с большим количеством кода, включая практическую реализацию
- Объектно-ориентированная реализация с тестами
- Исследование характеристик производительности структур данных дерева двоичного поиска (страницы 67-75) - Сравнение деревьев AA, красно-черных деревьев, треков, списков пропусков и оснований системы счисления.
- Реализация Objective-C
- Реализация Python