Реализация AVLTree (C ++): Могу ли я описать delete () как случай insert () для повторного использования кода?

0

Потерпите меня на этом, потому что я думаю, что это умеренно сложно объяснить:

Я построил следующую процедуру AVLTree :: Insert() в соответствии с реализацией R.Coleman:

void AVLTree::Insert(AVLTreeNode *newNode)  ///MARKER: ####### REMINDER ######
{                                           ///This implementation REQUIRES that newNode has its @left, @right and @parent pointers set to NULL *BEFORE* sending him here, as a parameter
    AVLTreeNode *temp, *prev, *ancestor;

    temp = root;                            //Our temp starts at the root, and keeps heading down the tree until it "falls out".
    prev = NULL;                            //@prev will "follow" @temp, one step behind it. It will, in the end, mark the point where we'll add newNode at.
    ancestor = NULL;                        //Ancestor marks the position of the closest @ancestor that will drop out of balance after we insert newNode.

    if(root == NULL )                       //Check if the tree is empty before you do anything else.
    {
        root = newNode;
        return;
    }
    //Looks like it isn't empty. Let start the main loop.
    while(temp != NULL)
    {
        prev = temp;
        if(temp->balanceFactor != '=')      //We found a node that is unbalanced, it'll drop out of balance completelly when we add the new node.
        {                                   //Let have the @ancestor variable point at it so we can restore the AVL property from the bottom to this node.
            ancestor = temp;
        }
        if(newNode->value < temp->value)    //These two ifs will throw @temp out of the tree at the end of the loop
        {                                   //while @prev will be pointing at the node below which we'll be adding newNode
            temp = temp->left;
        }
        else
        {
            temp = temp->right;
        }
    }
    ///The loop finished, @temp is now null. Time to insert newNode.
    newNode->parent = prev;
    if(newNode->value < prev->value)        //If it smaller than @prev, place it on its left, else do it at @prev right.
    {
        prev->left = newNode;
    }
    else
    {
        prev->right = newNode;
    }
    ///Now to restore the AVL property of the tree, starting from the inserted node up towards @ancestor, the last known unbalanced node that has now completely fallen out of balance.
    restoreAVL(ancestor,newNode);
}

Обратите внимание, что в конце я вызываю RestoreAVL, который принимает в качестве параметров newNode и предок (последний узел поддерживает дерево, которое нуждается в настройке, потому что он выпал из равновесия - в течение этого времени он указывает на узел (temp! = Null).)

Это AVLTree :: restoreAVL(): если вы все- таки читаете все это, он учитывает каждый случай, который может произойти, вставляя новый узел в AVLTree и при необходимости восстанавливая свойство AVL с поворотами и повторением - установить коэффициенты баланса (L, R или =)

void AVLTree::restoreAVL(AVLTreeNode *ancestor, AVLTreeNode *newNode)
{   ///This process restores the AVL property in the tree, from the bottom
    //-------------------------------------------------------
    // Case 1: ancestor is NULL, that means the balanceFactor of all ancestors is '='
    //-------------------------------------------------------
    if(ancestor == NULL)
    {
        if(newNode->value < root->value)
        {
            root->balanceFactor = 'L';  //newNode was inserted at the left of our root
        }                               //during our previous Insert
        else
        {
            root->balanceFactor = 'R';  //Here it on our right
        }
        ///Adjust the balanceFactor for all nodes from newNode back up to root
        adjustBalanceFactors(root, newNode);
    }
    //-------------------------------------------------------
    // Case 2: Insertion in opposite subtree of ancestor balance factor, i.e.
    // ancestor.balanceFactor == 'L' AND Insertion made in ancestor RIGHT subtree
    // OR
    // ancestor.balanceFactor == 'R' AND Insertion made in ancestor LEFT subtree
    // (In short, the insertion "neutralises" the balance of ancestor.)
    //-------------------------------------------------------
    else if(    ( (ancestor->balanceFactor == 'L') && (newNode->value > ancestor->value) )
                ||
                ( (ancestor->balanceFactor == 'R') && (newNode->value < ancestor->value) )
           )
    {
        ancestor->balanceFactor = '=';  //Ancestor balance factor is now neutralised.
        ///Adjust the balanceFactor for all nodes up to the ancestor,
        ///not up to the root like we did in Case 1.
        adjustBalanceFactors(ancestor,newNode);
    }
    //-------------------------------------------------------
    // Case 3: @ancestor balance is 'R' and the new node was inserted in the right subtree of @ancestor right child.
    // As expected, the balance is now broken and we need to rotate left, once.
    //-------------------------------------------------------
    else if( (ancestor->balanceFactor == 'R') && (newNode->value > ancestor->right->value) )
    {
        ancestor->balanceFactor = '=';  //We reset @ancestor balance, it will be adjusted by @adjustBalanceFactors()
        rotateLeft(ancestor);           //Single left rotation with ancestor as the pivot.
        ///Let adjust the balanceFactor for all nodes up to @ancestor PARENT.
        adjustBalanceFactors(ancestor->parent, newNode);
    }
    //-------------------------------------------------------
    // Case 4: @ancestor balance is 'L' and the node inserted is in the left subtree of @ancestor left child.
    // Here we have to rotate right, once. (Mirror case of Case 3 - See above)
    //-------------------------------------------------------
    else if( (ancestor->balanceFactor == 'L') && (newNode->value < ancestor->left->value) )
    {
        ancestor->balanceFactor = '=';  //As before, @ancestor balance needs to be reset.
        rotateRight(ancestor);
        ///Again, we adjust the balanceFactor for all nodes up to @ancestor PARENT.
        adjustBalanceFactors(ancestor->parent, newNode);
    }
    //-------------------------------------------------------
    // Case 5: @ancestor balance factor is "L" and the new node is inserted
    // in the RIGHT subtree of ancestor LEFT child
    //-------------------------------------------------------
    else if( (ancestor->balanceFactor == 'L') && (newNode->value > ancestor->left->value) )
    {
        rotateLeft(ancestor->left);
        rotateRight(ancestor);
        adjustLeftRight(ancestor,newNode);
    }
    //-------------------------------------------------------
    // Case 6 (final case): @ancestor balance factor is "R" and the new node is inserted
    // in the LEFT subtree of ancestor RIGHT child
    //-------------------------------------------------------
    else
    {
        rotateRight(ancestor->right);
        rotateLeft(ancestor);
        adjustRightLeft(ancestor,newNode);
    }
}

Поэтому мой вопрос: я хочу реализовать AVLTree :: Delete (AVLTreenode * n). Вместо того, чтобы разрушать мою голову, думая о каждом возможном исходе, если вы удаляете узел в AVLTree, могу ли я уменьшить Deletion() в случае Insertion() и вызвать RestoreAVL() с некоторым узлом, установленным как newNode, и один установлен как предок? Можно ли переработать restoreAVL()?

Некоторые примеры:

Изображение 174551

Результат тот же, если я думаю, что после игнорирования 00, 20 в поддерево.

Но позвольте добавить узел 70 в левое дерево и попытаться уменьшить Deletion() в Insную.

Изображение 174551

Я не могу придумать какой-либо алгоритмический способ уменьшения этой ситуации в Insную(), поэтому я знаю, кто может действовать как newNode и кто может быть предком, и вызвать restoreAVL().

Что я могу сказать? Есть ли отказоустойчивый способ уменьшения проблемы и, следовательно, сокращение кода, который я должен переписать?

Теги:
binary-tree
avl-tree

2 ответа

0

Если вы можете закодировать оператор вставки, вы сможете удалить код. Потому что удаление проще, чем вставка. Это действительно так, потому что вам не нужно перебалансировать оператор удаления. Сложность по-прежнему равна O (log N) для каждого запроса.

Мой код не слишком эффективен, но он достаточно короток для кодирования.

void doDelete(node* a){
    if (a==0) return ;
    if (a->ll==0) {
        if (a->pp==0 || a->pp->ll==a)
        lljoin(a->rr, a->pp);
        else rrjoin(a->rr, a->pp);
        delete a;
    }
    else if (a->rr==0){
        if (a->pp==0 || a->pp->ll==a)
        lljoin(a->ll, a->pp);
        else rrjoin(a->ll, a->pp);
        delete a;
    }
    else {
        node *b = rightMost(a->ll);
        swap(b->value, a->value);
        doDelete(b);
    }
}
  • 0
    После удаления сложность поиска не обязательно больше равна O (log N). Например, допустим, у вас есть большое полное дерево. Затем вы удаляете все, кроме самой правой ветви, без перебалансировки. В этом случае ваше дерево по сути является связанным списком с поиском O (N).
  • 0
    @tfinniga AVLTree Deletion () должен сохранять свойство AVL после каждого вызова, тем самым сохраняя время поиска O (logn). Но это не имеет значения.
Показать ещё 3 комментария
0

Очевидно, вы можете найти некоторые общие действия для удаления и вставки (например, поиск), но я думаю, что этого не стоит. Между этими алгоритмами все еще очень много. Вы, безусловно, будете повторно использовать restoreAVL для восстановления свойств AVL.

Насколько я понимаю, проблема в ваших примерах заключается в том, что когда вы удаляете узел из одного поддерева, вы хотите сбалансировать другое поддерево последнего сбалансированного узла на пути к удаленному узлу от корня (где сбалансированный узел является узлом с сбалансированнымFactor = '= "). Для меня это не имеет смысла, потому что это не правильно на первом месте и намного сложнее закодировать.

  • 0
    Сокращая удаление до случая вставки, я могу выяснить param newNode и param ancestor, что позволяет избежать перезаписи restoreAVL (newNode, ancestor). Это мой вопрос.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню