在计算机科学中,平衡二叉树(AVL树)是最先发明的自平衡二叉查找树。在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树。查找、插入和删除在平均和最坏情况下都是O(log n)。增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。AVL树得名于它的发明者是前苏联数学家G.M. Adelson-Velsky和E.M. Landis,他们在1962年的论文《An algorithm for the organization of information》中发表了它。
节点的平衡因子是它的右子树的高度减去它的左子树的高度。带有平衡因子1、0或 -1的节点被认为是平衡的。带有平衡因子 -2或2的节点被认为是不平衡的,并需要重新平衡这个树。平衡因子可以直接存储在每个节点中,或从可能存储在节点中的子树高度计算出来。
class AVLTree : public BST
{
protected:
int height;
int BalanceFactor () const;
void AdjustHeight ();
void LLRotation ();
void LRRotation ();
void RRRotation ();
void RLRotation ();
void AttachKey (Object&);
Object& DetachKey ();
void Balance ();
public:
AVLTree ();
int Height () const;
AVLTree& Left () const;
AVLTree& Right () const;
private:
int Max(int lft, int rt) const;
};
AVLTree::AVLTree():
BST(),
height(-1)
{}
int AVLTree::Height() const
{
return height;
}
void AVLTree::AdjustHeight()
{
if (IsEmpty ())
height = -1;
else
height = Max (left->Height (), right->Height ()) + 1;
}
int AVLTree::Max( int lft, int rt ) const
{
return (lft>rt)?lft:rt;
}
int AVLTree::BalanceFactor() const
{
if (IsEmpty ())
{
return 0;
}
else
{
return left->Height () - right->Height ();
}
}
void AVLTree::LLRotation()
{
if (IsEmpty ())
throw domain_error ("invalid rotation");
BinaryTree* const tmp = right;
right = left;
left = Right ().left;
Right ().left = Right ().right;
Right ().right = tmp;
Object* const tmpObj = key;
key = Right ().key;
Right ().key = tmpObj;
Right ().AdjustHeight ();
AdjustHeight ();
}
void AVLTree::LRRotation()
{
if (IsEmpty ())
throw domain_error ("invalid rotation");
Left ().RRRotation ();
LLRotation ();
}
void AVLTree::RRRotation()
{
if (IsEmpty ())
throw domain_error ("invalid rotation");
BinaryTree* const tmp = left;
left = right;
right = Left().right;
Left().right = Left().left;
Left().left = tmp;
Object* const tmpObj = key;
key = Left().key;
Left().key = tmpObj;
Left().AdjustHeight();
AdjustHeight();
}
void AVLTree::RLRotation()
{
if (IsEmpty ())
throw domain_error ("invalid rotation");
Right().LLRotation();
RRRotation();
}
void AVLTree::Balance()
{
AdjustHeight ();
if (abs (BalanceFactor ()) > 1)
{
if (BalanceFactor () > 0)
{
if (Left ().BalanceFactor () > 0)
LLRotation ();
else
LRRotation ();
}
else
{
if (Right ().BalanceFactor () < 0)
RRRotation ();
else
RLRotation ();
}
}
}
void AVLTree::AttachKey( Object& object )
{
if (!IsEmpty ())
throw domain_error ("invalid operation");
key = &object;
left = new AVLTree ();
right = new AVLTree ();
height = 0;
}
Object& AVLTree::DetachKey()
{
height = -1;
return BST::DetachKey ();
}
AVLTree& AVLTree::Left() const
{
if (IsEmpty())
{
throw domain_error("invalid operation");
}
return dynamic_cast<AVLTree&>(*left);
}
AVLTree& AVLTree::Right() const
{
if (IsEmpty())
{
throw domain_error("invalid operation");
}
return dynamic_cast<AVLTree&>(*right);
}
解释一下实现代码,AVL树在每次完成插入或删除之后,会进行一次平衡操作。这个操作会根据平衡因子的情况对节点进行旋转,如图:
向AVL树插入可以通过如同它是未平衡的二叉查找树一样把给定的值插入树中,接着自底向上向根节点折回,于在插入期间成为不平衡的所有节点上进行旋转来完成。因为折回到根节点的路途上最多有1.5乘log n个节点,而每次AVL旋转都耗费恒定的时间,插入处理在整体上耗费O(log n) 时间。
从AVL树中删除可以通过把要删除的节点向下旋转成一个叶子节点,接着直接剪除这个叶子节点来完成。因为在旋转成叶子节点期间最多有log n个节点被旋转,而每次AVL旋转耗费恒定的时间,删除处理在整体上耗费O(log n) 时间。
可以像普通二叉查找树一样的进行,所以耗费O(log n)时间,因为AVL树总是保持平衡的。不需要特殊的准备,树的结构不会由于查找而改变。(这是与伸展树查找相对立的,它会因为查找而变更树结构。)