2014 dxdy logo

Научный форум dxdy

Математика, Физика, Computer Science, Machine Learning, LaTeX, Механика и Техника, Химия,
Биология и Медицина, Экономика и Финансовая Математика, Гуманитарные науки




Начать новую тему Ответить на тему
 
 Балансировка (при вставке) АВЛ-дерева
Сообщение25.01.2010, 18:21 


08/11/09
156
Объясните, пожалуйста, доходчиво. Ну или сошлитесь. А то в Википедии непонятно, а одногруппники не дают переписать... Спасибо!

 Профиль  
                  
 
 Re: Балансировка (при вставке) АВЛ-дерева
Сообщение25.01.2010, 20:35 
Заслуженный участник


04/05/09
4582
Что именно в Википедии непонятно?

 Профиль  
                  
 
 Re: Балансировка (при вставке) АВЛ-дерева
Сообщение26.01.2010, 15:30 


08/11/09
156
Когда какое вращение применяется.

 Профиль  
                  
 
 Re: Балансировка (при вставке) АВЛ-дерева
Сообщение26.01.2010, 17:26 
Заслуженный участник


04/05/09
4582
Здесь это написано.

 Профиль  
                  
 
 Re: Балансировка (при вставке) АВЛ-дерева
Сообщение02.06.2012, 19:39 
Аватара пользователя


20/03/12
23
Не хотелось создавать новую тему, поэтому пишу здесь.
У меня проблема с пониманием удаления узла из АВЛ-дерева, алгоритм которого описан в Вики. Я не могу понять одну деталь: что означает в "найдем самую близкую по значению вершину в поддереве наибольшей высоты (правом или левом) и переместим её на место удаляемой вершины, при этом вызвав процедуру её удаления" слово переместим? Замена подразумевает обмен указателей на дочерние элементы, или просто ключей??

 Профиль  
                  
 
 Re: Балансировка (при вставке) АВЛ-дерева
Сообщение02.06.2012, 20:57 
Заслуженный участник
Аватара пользователя


01/08/06
3054
Уфа
Достаточно поменять ключи, т.к. при такой замене, очевидно, дерево остаётся деревом поиска, а большего здесь и не требуется.
Хотя если ключи длинные и менять их долго, быстрее будет совершить эквивалентную замену указателей.

 Профиль  
                  
 
 Re: Балансировка (при вставке) АВЛ-дерева
Сообщение02.06.2012, 22:48 
Аватара пользователя


20/03/12
23
Мой алгоритм работает неправильно, если удалять несбалансированный корневой элемент, потому что в этом случае не происходит балансировка, так как у него нет родителей. Делал вроде по классическому алгоритму, но не уверен что манипулирую правильно указателями при замещении... Хочется сделать элегантно, без многочисленных условий.
Код:
// удаление узла из дерева
TTree removeNode(TTree T, int key, bool* reduced) {
   TTree Q;
   TKey bkey;
   if (T == NULL)
      return NULL;
   if (key == T->key) {
      if (T->left == NULL) {
         Q = T->right;
         _release(T);
         *reduced = TRUE;
         return Q;
      }
      else if (T->right == NULL) {
         Q = T->left;
         _release(T);
         *reduced = TRUE;
         return Q;
      }
                // здесь идет замена. в зависимости баланса удаляемого узла, выбирается симметричный ей узел в левом или правом поддереве
      if (T->balance == L) {
         Q = getRightNode(T->left);
         bkey = Q->key;
         Q->key = T->key;
         T->key = bkey;
         T->left = removeNode(Q, key, reduced);
         T->balance = Q->balance;
         return T;
      }
      else {
         Q = getLeftNode(T->right);
         bkey = Q->key;
         Q->key = T->key;
         T->key = bkey;
         T->right = removeNode(Q, key, reduced);
         T->balance = Q->balance;
         return T;
      }
   }
   if (key < T->key) {
      T->left = removeNode(T->left, key, reduced);
      if (*reduced == TRUE) {
         switch (T->balance) {
            case B:
               T->balance = R;
               *reduced = FALSE;
               break;
            case L:
               T->balance = B;
               break;
            case R:
               if (T->right->balance == L)
                  T = leftBigRot(T);
               else
                  T = leftSmallRot(T);
               T->balance = B;
         };
      }
   }
   if (key > T->key) {
      T->right = removeNode(T->right, key, reduced);
      if (*reduced == TRUE) {
         switch (T->balance) {
            case B:
               T->balance = L;
               *reduced = FALSE;
               break;
            case R:
               T->balance = B;
               break;
            case L:
               if (T->left->balance == R)
                  T = rightBigRot(T);
               else
                  T = rightSmallRot(T);
               T->balance = B;
         };
      }
   }
   return T;
}

День на это потратил, обидно не разобраться!

 Профиль  
                  
 
 Re: Балансировка (при вставке) АВЛ-дерева
Сообщение03.06.2012, 16:53 
Заслуженный участник
Аватара пользователя


01/08/06
3054
Уфа
Не понял насчёт переменной L. Что это?
Если удаляется корневой узел (да и вообще любой не лист), то на этапе обмена узлов перебалансировка не нужна (хотя при последующем рекурсивном вызове может уже понадобиться удаление листа и перебалансировка).

 Профиль  
                  
 
 Re: Балансировка (при вставке) АВЛ-дерева
Сообщение03.06.2012, 21:55 
Аватара пользователя


20/03/12
23
Я конечно извиняюсь, что вылил код без контекста. L, B и R это константы перечисляемого типа TBalance, который устанавливает, в какую сторону узел имеет "перевес" (влево, баланс и вправо соответственно). Логично, балансировку нужно выполнять после вызова функции removeNode, где бы она не вызывалась.
Я собираюсь немного переделать алгоритм, в случае успеха или неудачи все равно отпишусь.

 Профиль  
                  
 
 Re: Балансировка (при вставке) АВЛ-дерева
Сообщение08.06.2012, 16:59 
Аватара пользователя


20/03/12
23
Вот моя версия удаления с балансировкой. Фишка в использовании стека для запоминания пути от удаляемого узла к его inorder predecessor, замена ключей удаляемого узла и inorder predecessor, рекурсивное удаление inorder predecessor и наконец обратный ход с балансировкой. Функции removed_from_left и removed_from_right используются соответственно при удалении узла из левого и правого поддерева. Они просто назначают новый баланс узлу и решают поворачивать дерево или нет.
код: [ скачать ] [ спрятать ]
Используется синтаксис C
TTree removeNode(TTree T, int akey, bool* reduced) {
        if (T == NULL)
                return NULL;
        if (akey == T->key) {
                TTree Q, track[MAX_TREE_HEIGHT];
                int top;
                TKey bkey;
                if (T->left == NULL) { // just throw T and put its right subtree instead of it
                        Q = T->right;
                        _release(T);
                        *reduced = TRUE; // mark subtree height reducing
                        return Q;
                }
                top = -1;
                track[++top] = T; // T is on the bottom of the track
                //       T - target                  --
                //    x                                |
                //       x                             | track
                //          x                          |
                //             Q - substitute        --
                //                0
                Q = T->left;
                while (Q != NULL) { // getting inorder predecessor and tracking path to it
                        track[++top] = Q;
                        Q = Q->right;
                };
                Q = track[top]; // swap keys of the target node and the substitute node
                bkey = T->key;
                T->key = Q->key;
                Q->key = bkey;
                if (Q != T->left) // remove the substitute node (Q = track[top])
                        track[--top]->right = removeNode(Q, akey, reduced);
                else
                        track[--top]->left = removeNode(Q, akey, reduced); // here track[top-1] is T
                while (top >= 0 && *reduced == TRUE) { // balance track while depth of a subtree is reduced
                        Q = track[top];
                        if (Q != T) { // came from right
                                if (Q != T->left)
                                        track[top-1]->right = removed_from_right(Q, reduced);
                                else
                                        T->left = removed_from_right(T->left, reduced);
                        }
                        else // came from left
                                T = removed_from_left(T, reduced);
                        top--; // up to T
                }
                return T;
        }
        if (akey < T->key) { // search
                T->left = removeNode(T->left, akey, reduced);
                if (*reduced == TRUE) // if T->left has been unbalanced, balance it
                        T = removed_from_left(T, reduced);
        }
        if (akey > T->key) {
                T->right = removeNode(T->right, akey, reduced);
                if (*reduced == TRUE)
                        T = removed_from_right(T, reduced);
        }
        return T;
}

К минусу отношу необходимость создавания массива для хранения трека (стека) при каждом переходе от удаляемого к заменяемому узлу и многочисленные условия, связанные с тем, что трек неоднородный по направлению (не каждый i элемент трека является правым потомком i-1, это справедливо только для i>1).
Кстати, процедура добавления узла в АВЛ дерево на Вики не доработана. Там при малом правом и левом поворотах, бывшему корневому узлу (да и новому) присваивается нулевой баланс, что не всегда справедливо (если L = C это не справедливо). Может это прокатывает при добавлении, но при удалении я столкнулся с разбалансировкой дерева из-за этого недочета.

 Профиль  
                  
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 10 ] 

Модераторы: Karan, Toucan, PAV, maxal, Супермодераторы



Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group