Дерево AA - AA tree

An Дерево AA в Информатика это форма сбалансированное дерево используется для эффективного хранения и извлечения упорядоченных данных. Деревья AA названы в честь Арне Андерссон, их изобретатель.

Деревья AA представляют собой разновидность красно-черное дерево, форма двоичное дерево поиска который поддерживает эффективное добавление и удаление записей. В отличие от красно-черных деревьев, красные узлы в дереве AA можно добавить только как правый дочерний элемент. Другими словами, никакой красный узел не может быть левым дочерним элементом. Это приводит к моделированию 2-3 дерева вместо 2-3-4 дерево, что значительно упрощает обслуживание. Алгоритмы обслуживания красно-черного дерева должны учитывать семь различных форм, чтобы правильно сбалансировать дерево:

Красный Черный Форма Cases.svg

С другой стороны, дерево AA должно учитывать только две формы из-за строгого требования, что только правые ссылки могут быть красными:

AA Tree Shape Cases.svg

Балансировка вращений

В то время как красно-черные деревья требуют одного бита балансирующих метаданных на узел (цвет), деревья AA требуют O (log (log (N))) бит метаданных на узел в форме целочисленного «уровня». Для AA-деревьев имеют место следующие инварианты:

  1. Уровень каждого листового узла равен единице.
  2. Уровень каждого левого потомка ровно на единицу меньше, чем у его родителя.
  3. Уровень каждого правого ребенка равен уровню его родителя или на единицу меньше.
  4. Уровень каждого правого внука строго ниже, чем у его прародителя.
  5. Каждый узел уровня выше одного имеет двух дочерних узлов.

Ссылка, в которой уровень ребенка равен уровню его родителя, называется горизонтальный ссылка, и аналогична красной ссылке в красно-черном дереве. Разрешены отдельные правые горизонтальные ссылки, но запрещены последовательные; все левые горизонтальные ссылки запрещены. Это более строгие ограничения, чем аналогичные ограничения для красно-черных деревьев, в результате чего повторная балансировка дерева AA процедурно намного проще, чем повторная балансировка красно-черного дерева.

Вставки и удаления могут временно привести к тому, что дерево AA станет несбалансированным (то есть нарушит инварианты дерева AA). Для восстановления баланса нужны всего две различные операции: «перекос» и «разбиение». Наклон - это поворот вправо для замены поддерева, содержащего левую горизонтальную ссылку, на поддерево, содержащее правую горизонтальную ссылку. Разделение - это поворот влево и повышение уровня для замены поддерева, содержащего две или более последовательных правых горизонтальных ссылки, одной, содержащей на две последовательных правых горизонтальных ссылки меньше. Реализация вставки и удаления с сохранением баланса упрощается за счет использования операций перекоса и разделения для изменения дерева только в случае необходимости, вместо того, чтобы заставлять их вызывающие стороны решать, перекосить или разделить.

функция перекос является    Вход: T, узел, представляющий дерево AA, которое необходимо перебалансировать. выход: Еще один узел, представляющий ребалансированное дерево AA. если ноль (T) тогда        возвращаться Ноль иначе если ноль (слева (T)) тогда        возвращаться Т иначе если уровень (слева (T)) == уровень (T) тогда        Поменяйте местами указатели горизонтальных левых ссылок.        L = влево (T) влево (T): = вправо (L) вправо (L): = T возвращаться L еще        возвращаться Т конец, есликонечная функция

Перекос: AA Tree Skew2.svg

функция расколоть является    Вход: T, узел, представляющий дерево AA, которое необходимо перебалансировать. выход: Еще один узел, представляющий ребалансированное дерево AA. если ноль (T) тогда        возвращаться Ноль иначе если ноль (справа (T)) или же  ноль (справа (справа (T))) тогда        возвращаться Т иначе если уровень (T) == уровень (справа (справа (T))) тогда        У нас есть две горизонтальные правые ссылки. Возьмите средний узел, поднимите его и верните.        R = правый (T) правый (T): = левый (R) левый (R): = T уровень (R): = уровень (R) + 1 возвращаться р еще        возвращаться Т конец, есликонечная функция

Расколоть: AA Tree Split2.svg

Вставка

Вставка начинается с обычной процедуры поиска и вставки в двоичное дерево. Затем, когда стек вызовов раскручивается (при условии рекурсивной реализации поиска), легко проверить правильность дерева и при необходимости выполнить любые вращения. Если возникает горизонтальная левая ссылка, выполняется перекос, а если возникают две горизонтальные правые связи, выполняется разделение, возможно, увеличивая уровень нового корневого узла текущего поддерева. Обратите внимание, что в приведенном выше коде приращение уровня (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, что делает их удаление тривиальным.

Чтобы сбалансировать дерево, есть несколько подходов. Тот, что описал Андерссон в его оригинальная бумага - самый простой, и он описан здесь, хотя в реальных реализациях может быть выбран более оптимизированный подход. После удаления первым шагом к поддержанию достоверности дерева является понижение уровня всех узлов, чьи дочерние элементы находятся на два уровня ниже их, или у которых отсутствуют дочерние элементы. Затем весь уровень нужно перекосить и разделить. Этот подход получил поддержку, потому что в концептуальном плане он включает три легко понимаемых отдельных шага:

  1. При необходимости уменьшите уровень.
  2. Наклоните уровень.
  3. Разделите уровень.

Однако на этот раз мы должны перекосить и разделить весь уровень, а не только узел, что усложняет наш код.

функция Удалить является    Вход: 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]

Смотрите также

Рекомендации

  1. ^ "Исследование характеристик производительности структур данных дерева двоичного поиска (страницы 67-75)" (PDF). Архивировано из оригинал (PDF) на 2014-03-27. Получено 2010-10-17.

внешняя ссылка