在计算机科学中,AVL树是最先发明的自平衡二叉查找树。在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树。查找、插入和删除在平均和最坏情况下都是O(logn)。增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。AVL树得名于它的发明者G.M. Adelson-Velsky和E.M. Landis,他们在1962年的论文《An algorithm for the organization of information》中发表了它。
节点的平衡因子是它的左子树的高度减去它的右子树的高度(有时相反)。带有平衡因子1、0或 -1的节点被认为是平衡的。带有平衡因子 -2或2的节点被认为是不平衡的,并需要重新平衡这个树。平衡因子可以直接存储在每个节点中,或从可能存储在节点中的子树高度计算出来。
基本的数据结构:
typedef struct Node
{
Node* left;
Node* right;
int height;
int data;
Node(int dat)
{
left = right = NULL;
data = dat;
height = 1;
}
}*pNode, Node;
int Height(pNode p)
{
if (p!=NULL)
return p->height;
return 0;
}
操作:
AVL树的基本操作一般涉及运作同在不平衡的二叉查找树所运作的同样的算法。但是要进行预先或随后做一次或多次所谓的"AVL旋转"。
旋转操作:
//右旋转
pNode RightRotate(pNode a)
{
pNode b = a->left;
a->left = b->right;
b->right = a;
a->height = max( Height(a->left), Height(a->right) ) + 1;
b->height = max( Height(b->left), a->height ) + 1;
return b;
}
//左旋转
pNode LeftRotate(pNode a)
{
pNode b = a->right;
a->right = b->left;
b->left = a;
a->height = max( Height(a->left), Height(a->right) ) + 1;
b->height = max( a->height, Height(b->right) ) + 1;
return b;
}
//左旋转,右旋转
pNode LeftRightRotate(pNode a)
{
a->left = LeftRotate(a->left);
return RightRotate(a);
}
//右旋转,左旋转
pNode RightLeftRotate(pNode a)
{
a->right = RightRotate(a->right);
return LeftRotate(a);
}
插入:
向AVL树插入可以通过如同它是未平衡的二叉查找树一样把给定的值插入树中,接着自底向上向根节点折回,于在插入期间成为不平衡的所有节点上进行旋转来完成。因为折回到根节点的路途上最多有1.5乘logn个节点,而每次AVL旋转都耗费恒定的时间,插入处理在整体上耗费O(logn) 时间。
pNode Insert(int data, pNode p)
{
if (p==NULL)
{
p = new Node(data);
}
else if (data < p->data)
{
p->left = Insert(data, p->left);
if (Height(p->left)-Height(p->right) > 1)
{
if (data < p->left->data)
p = RightRotate(p);
else
p = LeftRightRotate(p);
}
}
else
{
p->right = Insert(data, p->right);
if (Height(p->right)-Height(p->left) > 1)
{
if (data > p->right->data)
p = LeftRotate(p);
else
p = RightLeftRotate(p);
}
}
return p;
}
删除:
从AVL树中删除可以通过把要删除的节点向下旋转成一个叶子节点,接着直接剪除这个叶子节点来完成。因为在旋转成叶子节点期间最多有log n个节点被旋转,而每次AVL旋转耗费恒定的时间,删除处理在整体上耗费O(logn) 时间。
查找:
可以像普通二叉查找树一样的进行,所以耗费O(log n)时间,因为AVL树总是保持平衡的。不需要特殊的准备,树的结构不会由于查找而改变。