AA-дерево — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
Строка 38: Строка 38:
 
#'''Skew(T)''' {{---}} устранение левого горизонтального ребра.
 
#'''Skew(T)''' {{---}} устранение левого горизонтального ребра.
  
[[Файл: Skew.png]]
+
  '''function''' skew(T)
 
 
 
 
  '''function''' skew '''is'''
 
    '''input''': T, a node representing an AA tree that needs to be rebalanced.
 
    '''output''': Another node representing the rebalanced AA tree.
 
 
 
     '''if''' T == NULL '''then'''
 
     '''if''' T == NULL '''then'''
 
         '''return''' NULL
 
         '''return''' NULL
Строка 50: Строка 44:
 
         '''return''' T
 
         '''return''' T
 
     '''else''' '''if''' level(left(T)) == level(T) '''then'''
 
     '''else''' '''if''' level(left(T)) == level(T) '''then'''
         Swap the pointers of horizontal left links.
+
         <font color=green> //Меняем указатель горизонтального левого ребра</font>
 
         L = left(T)
 
         L = left(T)
 
         left(T) := right(L)
 
         left(T) := right(L)
Строка 60: Строка 54:
 
  '''end''' '''function'''
 
  '''end''' '''function'''
  
 +
[[Файл: Skew.png]]
  
 
#'''Split(T)''' {{---}} устранение двух последовательных правых горизонтальных ребер.
 
#'''Split(T)''' {{---}} устранение двух последовательных правых горизонтальных ребер.
  
[[Файл: Split_rb.png]]
+
  '''function''' split(T)
 
 
  '''function''' split '''is'''
 
    '''input''': T, a node representing an AA tree that needs to be rebalanced.
 
    '''output''': Another node representing the rebalanced AA tree.
 
 
 
     '''if''' nil(T) '''then'''
 
     '''if''' nil(T) '''then'''
 
         '''return''' Nil
 
         '''return''' Nil
Строка 74: Строка 64:
 
         '''return''' T
 
         '''return''' T
 
     '''else''' '''if''' level(T) == level(right(right(T))) '''then'''
 
     '''else''' '''if''' level(T) == level(right(right(T))) '''then'''
         We have two horizontal right links. Take the middle node, elevate it, and return it.
+
         <font color=green> //Существует два правых горизонтальных ребра. Берем центральную вершину, "поднимаем" ее и возвращаем указатель на нее</font>
 
         R = right(T)
 
         R = right(T)
 
         right(T) := left(R)
 
         right(T) := left(R)
Строка 84: Строка 74:
 
     '''end''' '''if'''
 
     '''end''' '''if'''
 
  '''end function'''
 
  '''end function'''
 +
 +
[[Файл: Split_rb.png]]
  
 
== Операции ==
 
== Операции ==
Строка 89: Строка 81:
 
Рекурсивная реализация. Спускаемся от корня вниз по дереву, сравнивая ключи; вставляем новую вершину; выходя из рекурсии и выполняем балансировку: skew и split для каждой вершины.
 
Рекурсивная реализация. Спускаемся от корня вниз по дереву, сравнивая ключи; вставляем новую вершину; выходя из рекурсии и выполняем балансировку: skew и split для каждой вершины.
  
  '''function''' insert '''is'''
+
  '''function''' insert(X, T)
    '''input''': X, the value to be inserted, and T, the root of the tree to insert it into.
+
     <font color=green>//X - вставляемое значение, Т - корень дерева, в который вставляется вершина</font>
     '''output''': A balanced version T including X.
 
 
   
 
   
    Do the normal binary tree insertion procedure. Set the result of the
 
    recursive call to the correct child in case a new node was created or the
 
    root of the subtree changes.
 
 
     '''if''' nil(T) '''then'''
 
     '''if''' nil(T) '''then'''
 
         Create a new leaf node with X.
 
         Create a new leaf node with X.
Строка 104: Строка 92:
 
         right(T) := insert(X, right(T))
 
         right(T) := insert(X, right(T))
 
     '''end if'''
 
     '''end if'''
     Note that the case of X == value(T) is unspecified. As given, an insert
+
     <font color=green>//Случай X == value(T) не определен. Т.е. вставка не будет иметь никакого эффекта, возможны различные варианты обработки, в зависимости от решаемой задачи</font>
    will have no effect. The implementor may desire different behavior.
 
 
   
 
   
    Perform skew and then split. The conditionals that determine whether or
 
    not a rotation will occur or not are inside of the procedures, as given
 
    above.
 
 
     T := skew(T)
 
     T := skew(T)
 
     T := split(T)
 
     T := split(T)
Строка 125: Строка 109:
 
Чтобы сохранять баланс дерева необходимо делать skew, split и корректировку уровня для каждой вершины.
 
Чтобы сохранять баланс дерева необходимо делать skew, split и корректировку уровня для каждой вершины.
  
  '''function''' delete '''is'''
+
  '''function''' delete(X, T)
    '''input''': X, the value to delete, and T, the root of the tree from which it should be deleted.
+
     <font color=green>//X - удаляемый элемент, Т - корень дерева, из которого он должен быть удален</font>
     '''output''': T, balanced, without the value X.
 
 
      
 
      
 
     '''if''' nil(T) '''then'''
 
     '''if''' nil(T) '''then'''
Строка 136: Строка 119:
 
         left(T) := delete(X, left(T))
 
         left(T) := delete(X, left(T))
 
     '''else'''
 
     '''else'''
        If we're a leaf, easy, otherwise reduce to leaf case.
 
 
         '''if''' leaf(T) '''then'''
 
         '''if''' leaf(T) '''then'''
 
             '''return''' Nil
 
             '''return''' Nil
Строка 149: Строка 131:
 
         '''end if'''
 
         '''end if'''
 
     '''end if'''
 
     '''end if'''
+
   
     Rebalance the tree. Decrease the level of all nodes in this level if
+
     <font color=green>//Сбалансируем дерево. Если необходимо, уменьшим поля "уровень" у вершин на данном уровне, и затем skew и split все вершины на новом уровне</font>
    necessary, and then skew and split all nodes in the new level.
 
 
     T := decrease_level(T)
 
     T := decrease_level(T)
 
     T := skew(T)
 
     T := skew(T)
Строка 163: Строка 144:
 
  '''end function'''
 
  '''end function'''
  
  '''function''' decrease_level '''is'''
+
  '''function''' decrease_level(T)
    '''input''': T, a tree for which we want to remove links that skip levels.
 
    '''output''': T with its level decreased.
 
 
   
 
   
 
     should_be = min(level(left(T)), level(right(T))) + 1
 
     should_be = min(level(left(T)), level(right(T))) + 1
Строка 183: Строка 162:
 
== Эффективность ==
 
== Эффективность ==
 
Скорость работы AA - дерева эквивалентна скорости работы красно-черного дерева. В среднем более простые алгоритмы на AA - дерева выполняются быстрее, но в красно-черном дереве делается меньше поворотов, что уравновешивает асимптотику.
 
Скорость работы AA - дерева эквивалентна скорости работы красно-черного дерева. В среднем более простые алгоритмы на AA - дерева выполняются быстрее, но в красно-черном дереве делается меньше поворотов, что уравновешивает асимптотику.
 +
 +
== См. также ==
 +
* [[B-дерево]]
 +
* [[Splay-дерево]]
 +
* [[АВЛ-дерево]]
 +
* [[Декартово дерево]]
 +
* [[Красно-черное дерево]]
 +
 +
 +
== Источники информации ==
 +
* [http://user.it.uu.se/~arnea/ps/simp.pdf {{---}} Balanced searched trees made simple]
 +
* [https://en.wikipedia.org/wiki/AA_tree {{---}} AA - tree]
 +
* [https://habrahabr.ru/post/110212/ {{---}} AA-Tree или простое бинарное дерево]
 +
 +
 +
 +
[[Категория:Дискретная математика и алгоритмы]]
 +
[[Категория:Структуры данных]]
 +
[[Категория:Деревья поиска]]

Версия 02:04, 20 декабря 2016

АA-дерево (англ. AA-Tree) — структура данных, представляющая собой сбалансированное двоичное дерево поиска, которое является разновидностью красно-черного дерева с дополнительными ограничениями.

АA-дерево названо по первым буквам имени и фамилии изобретателя, Арне Андерссона, который впервые предложил данную модификацию B-дерева в 1993 году.


Определение:
Уровень вершины (англ. Level) - вертикальная высота соответствующей вершины. Уровень листа равен 1.


В отличие от красно-черных деревьев, к одной вершине можно присоединить вершину только того же уровня, только одну и только справа (другими словами, красные вершины могут быть добавлены только в качестве правого ребенка).

Свойства

  • Уровень каждого листа равен 1.
  • Уровень каждого левого ребенка ровно на один меньше, чем у его родителя.
  • Уровень каждого правого ребенка равен или один меньше, чем у его родителя.
  • Уровень каждого правого внука строго меньше, чем у его прародителя.
  • Каждая вершина с уровнем больше 1 имеет двоих детей.

Для поддержки баланса красно-черного дерева необходимо обрабатывать 7 различных вариантов расположения вершин:

Rb3.png

В АА-дереве из-за строгих ограничений необходимо обрабатывать только два вида возможных расположений вершин:

Rb2.png

Балансировка

Определение:
Горизонтальное ребро (англ. Horizontal edges) - ребро, соединяющее вершины с одинаковым уровнем.


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


Для балансировки АА-дерева нужны следующие две операции:

  1. Skew(T) — устранение левого горизонтального ребра.
function skew(T)
   if T == NULL then
       return NULL
   else if left(T) == NULL then
       return T
   else if level(left(T)) == level(T) then
        //Меняем указатель горизонтального левого ребра
       L = left(T)
       left(T) := right(L)
       right(L) := T
       return L
   else
       return T
   end if
end function

Skew.png

  1. Split(T) — устранение двух последовательных правых горизонтальных ребер.
function split(T)
   if nil(T) then
       return Nil
   else if nil(right(T)) or  nil(right(right(T))) then
       return T
   else if level(T) == level(right(right(T))) then
        //Существует два правых горизонтальных ребра. Берем центральную вершину, "поднимаем" ее и возвращаем указатель на нее
       R = right(T)
       right(T) := left(R)
       left(R) := T
       level(R) := level(R) + 1
       return R
   else
       return T
   end if
end function

Split rb.png

Операции

Вставка элемента

Рекурсивная реализация. Спускаемся от корня вниз по дереву, сравнивая ключи; вставляем новую вершину; выходя из рекурсии и выполняем балансировку: skew и split для каждой вершины.

function insert(X, T)
   //X - вставляемое значение, Т - корень дерева, в который вставляется вершина

   if nil(T) then
       Create a new leaf node with X.
       return node(X, 1, Nil, Nil)
   else if X < value(T) then
       left(T) := insert(X, left(T))
   else if X > value(T) then
       right(T) := insert(X, right(T))
   end if
   //Случай X == value(T) не определен. Т.е. вставка не будет иметь никакого эффекта, возможны различные варианты обработки, в зависимости от решаемой задачи

   T := skew(T)
   T := split(T)

   return T
end function

Пример вставки нового элемента (на рис. уровни разделены горизонтальными линиями):

Ex insert.png

Удаление вершины

Рекурсивная реализация. Как и в большинстве сбалансированных бинарных деревьев, удаление внутренней вершины можно заменить на удаление листа, если заменить внутреннюю вершину на ее ближайшего "предшественника" (англ. predecessor) или "преемника" (англ. successor), в зависимости от реализации. "Предшественник" находиться в начале последнего левого ребра, после которого идут все правые ребра. По аналогии, "преемник" может быть найден после одного правого ребра и последовательности левых ребер, пока не будет найден указатель на NULL. В силу свойства всех узлов уровня более чем 1, имеющих двух детей, предшественник или преемник будет на уровне 1, что делает их удаление тривиальным.

Чтобы сохранять баланс дерева необходимо делать skew, split и корректировку уровня для каждой вершины.

function delete(X, T)
   //X - удаляемый элемент, Т - корень дерева, из которого он должен быть удален
    
   if nil(T) then
       return T
   else if X > value(T) then
       right(T) := delete(X, right(T))
   else if X < value(T) then
       left(T) := delete(X, left(T))
   else
       if leaf(T) then
           return Nil
       else if nil(left(T)) then
           L := successor(T)
           right(T) := delete(value(L), right(T))
           value(T) := value(L)
       else
           L := predecessor(T)
           left(T) := delete(value(L), left(T))
           value(T) := value(L)
       end if
   end if
   
   //Сбалансируем дерево. Если необходимо, уменьшим поля "уровень" у вершин на данном уровне, и затем skew и split все вершины на новом уровне
   T := decrease_level(T)
   T := skew(T)
   right(T) := skew(right(T))
   if not nil(right(T))
       right(right(T)) := skew(right(right(T)))
   end if
   T := split(T)
   right(T) := split(right(T))
   return T
end function
function decrease_level(T)

   should_be = min(level(left(T)), level(right(T))) + 1
   if should_be < level(T) then
       level(T) := should_be
       if should_be < level(right(T)) then
           level(right(T)) := should_be
       end if
   end if
   return T
end function

Пример удаления вершины (на рис. уровни разделены горизонтальными линиями):

Exdelete.png

Эффективность

Скорость работы AA - дерева эквивалентна скорости работы красно-черного дерева. В среднем более простые алгоритмы на AA - дерева выполняются быстрее, но в красно-черном дереве делается меньше поворотов, что уравновешивает асимптотику.

См. также


Источники информации