Потерпите меня на этом, потому что я думаю, что это умеренно сложно объяснить:
Я построил следующую процедуру 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()?
Некоторые примеры:
Результат тот же, если я думаю, что после игнорирования 00, 20 в поддерево.
Но позвольте добавить узел 70 в левое дерево и попытаться уменьшить Deletion() в Insную.
Я не могу придумать какой-либо алгоритмический способ уменьшения этой ситуации в Insную(), поэтому я знаю, кто может действовать как newNode и кто может быть предком, и вызвать restoreAVL().
Что я могу сказать? Есть ли отказоустойчивый способ уменьшения проблемы и, следовательно, сокращение кода, который я должен переписать?
Если вы можете закодировать оператор вставки, вы сможете удалить код. Потому что удаление проще, чем вставка. Это действительно так, потому что вам не нужно перебалансировать оператор удаления. Сложность по-прежнему равна 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);
}
}
Очевидно, вы можете найти некоторые общие действия для удаления и вставки (например, поиск), но я думаю, что этого не стоит. Между этими алгоритмами все еще очень много. Вы, безусловно, будете повторно использовать restoreAVL для восстановления свойств AVL.
Насколько я понимаю, проблема в ваших примерах заключается в том, что когда вы удаляете узел из одного поддерева, вы хотите сбалансировать другое поддерево последнего сбалансированного узла на пути к удаленному узлу от корня (где сбалансированный узел является узлом с сбалансированнымFactor = '= "). Для меня это не имеет смысла, потому что это не правильно на первом месте и намного сложнее закодировать.