AVL树的定义:
一颗AVL树或者是一颗空树,或者是一颗具有下列性质的二叉搜索树:它的左子树和右子树都是一颗AVL树,并且左子树和右子树的高度之差的绝对值不超过1。
我们来对AVL树进行结构的设计:
typedef struct AVLNode
{
AVLNode* leftchild;//左孩子
AVLNode* rightchild;//有孩子
AVLNode* parent;//双亲
int data;//数据域
}AVLNode;
typedef struct
{
AVLNode* head;//头节点
int cursize;//当前树的节点个数
}AVLtree;
节点的平衡因子:
- 给每个节点附加一个数字,给出该节点右子树的高度减去左子树的高度所得的高度差。这个数字即为节点的平衡因子。
- 根据AVL树的定义,任一节点的平衡因子只能取-1,0,1三种。
- 如果一个节点的平衡因子的绝对值大于1,则这颗二叉树就失去了平衡,不再是一个AVL树。
- 如果一颗二叉搜索树是高度平衡的,它就称为AVL树,如果它有n个节点,其高度可保持在O(log2n),平均搜索长度也可保持在O(log2n)。
如图下图所示的高度不平衡的二叉搜索树和高度平衡的二叉搜索树:
平衡化旋转:
- 如果在一颗平衡的二叉搜索树中插入了一个新节点,造成了不平衡,此时必须调整树的结构,使其平衡化。
- 平衡化旋转有两类:
单旋转(左旋和右旋)
双旋转(左平衡和右平衡) - 每插入一个新节点时,AVL树中相关节点的平衡状态就会发生改变。因此在插入一个新节点后,需要从插入位置沿通向根的路径回溯,检查各节点的平衡因子(左右子树的高度差)。
- 如果在某一节点发现高度不平衡,停止回溯。
- 从发生不平衡的节点起,沿刚才回溯的路径取直接下两层的节点。
- 如果这三个节点处于一条直线上,则采用单旋转进行平衡化。单旋转可按方向分为左单旋转和右单旋转,其中一个是另一个的镜像,其方向与不平衡的形状相关。
- 如果这三个节点处于一条折线上,则采用双旋转进行平衡化。双旋转分为先左后右和先右后左两类。
下面是两个左单旋转的例子:
代码如下:
void RotateLeft(AVLNode * &ptr)
{
AVLNode* newroot = ptr->rightchild;
newroot->parent = ptr->parent;
ptr->rightchild = newroot->leftchild;
if (newroot->leftchild != NULL)
{
newroot->leftchild->parent = ptr;
}
newroot->leftchild = ptr;
ptr->parent = newroot;
ptr = newroot;
}
右单旋转:
代码如下:
void RotateRight(AVLNode*& ptr)
{
AVLNode* newroot = ptr->leftchild;
newroot->parent = ptr->parent;
ptr->leftchild = newroot->rightchild;
if (newroot->rightchild != NULL)
{
newroot->rightchild->parent = ptr;
}
newroot->rightchild = ptr;
ptr->parent = newroot;
ptr = newroot;
}