基本概念
- ASL:一颗搜索树查找元素的平均查找次数。
- 平衡因子(BF):BF(T) = hL–hR。其中hL,hR分别为左右子树高度。
定义
平衡二叉树也是一颗搜索树,它的ASL相对来说会小一些。满足以下特性:
- 是一颗空树,或者任一结点左、右子树高度差的绝对值不超过1,即|BF(T)|≤1。
/* AVL树结点定义 */
typedef struct node* AVLTree;
struct node
{
int data;
int height;/* 比BST多一个高度 */
AVLTree left;
AVLtree right;
};
如下图就是一颗AVL树:
操作
AVL树的一些基本操作如插入、删除等与BST类似。下面主要介绍如何将一颗BST调整为AVL树。
在插入一个元素的时候,有可能会把一颗AVL树的平衡破坏,这时候就要调整。分下面4种情况:
- 右单旋
- 特点:不平衡的“发现者”是1,“麻烦节点”3在发现者的右子树的右子树上,因而叫RR插入,需要RR旋转(右单旋)。
- 调整:
将被破坏结点的右子树提到上面去,即将B提到A的上面。B的右子树依然挂在B的右边,而B的左子树,由于比B小、比A大,所以调整之后挂在A的右子树上。
代码如下:
/* 右单旋 */
AVLTree SingleRightRotation(AVLTree A)
{
AVLTree B = A->right;//B在A的右子树上,先存起来
A->right = B->left;//把B的左子树挂在A的右子树上
B->left = A;//把B提到A的上面去
/* 由于现在A是B的左子树,先更新A的树高、再更新B的树高 */
A->height = max(getheight(A->left), getheight(A->right)) + 1;
B->height = max(getheight(B->right), A->height) + 1;
return B;
}
- 左单旋
分析类似,调整:将B提到A的上面,把B的右子树挂在A的左子树上。
/* 左单旋 */
AVLTree SingleLeftRotation(AVLTree A)
{
AVLTree B = A->left;
A->left = B->right;
B->right = A;
A->height = max(getheight(A->left), getheight(A->right)) + 1;
B->height = max(getheight(B->left), A->height)) + 1;
return B;
}
- 左右双旋
- 特点:不平衡的“发现者”是8,“麻烦节点"7在8的左子树的右子树上,因而叫LR插入,需要LR旋转。
- 调整:
代码如下:
/* 左右双旋 */
AVLTree DoubleLeftRightRotation(AVLTree A)
{
/* 左两次单旋 :
先将B、C做右单旋,此时A的左孩子是C,C的左孩子是B,C被返回;
再将A、C做左单旋,C被返回 */
A->left = SingleRightRotation(A->left);
return SingleLeftRotation(A);
}
- 右左双旋
直接给出代码:
/* 右左双旋 */
AVLTree DoubleRightLeftRotation(AVLTree A)
{
A->right = SingLeftRotation(A->right);
return SingRightRotation(A);
}
模板
最后给出插入元素的过程中将树调整为AVL树的代码:
#include <stdio.h>
#include <stdlib.h>
typedef struct node* avl;
struct node
{
int data;
int height;
avl left;
avl right;
};
int max(int a, int b)
{
return a > b ? a : b;
}
int getheight(avl t)
{
if(!t) return -1;/* 结点为空时,返回-1 */
return t->height;
}
avl sl(avl A)/* 左单旋 */
{
avl B = A->left;
A->left = B->right;
B->right = A;
A->height = max(getheight(A->left), getheight(A->right)) + 1;
B->height = max(getheight(B->left), A->height) + 1;
return B;
}
avl sr(avl A)/* 右单旋 */
{
avl B = A->right;
A->right = B->left;
B->left = A;
A->height = max(getheight(A->left), getheight(A->right)) + 1;
B->height = max(getheight(B->right), A->height) + 1;
return B;
}
avl dlr(avl A)/* 左右双旋 */
{
A->left = sr(A->left);
return sl(A);
}
avl drl(avl A)/* 右左双旋 */
{
A->right = sl(A->right);
return sr(A);
}
avl Insert(avl t, int x)/* 插入元素并调整树 */
{
if(!t)
{
t = (avl)malloc(sizeof(struct node));
t->data = x;
t->height = 0;
t->left = t->right = NULL;
}/* 找到了插入位置,生成结点,存入信息 */
else
if(x < t->data)
{
/* 插入左子树 */
t->left = Insert(t->left, x);
/* 左子树的高度已经比右子树高2了,即需要左旋 */
if(getheight(t->left) - getheight(t->right) == 2)
{
/* 要分情况看插在了哪边 */
if(x < t->left->data)/* 左子树的左子树,即左单旋 */
t = sl(t);
else /* 左子树的右子树,即左右双旋 */
t = dlr(t);
}
}
else if(x > t->data)
{
/* 插入右子树 */
t->right = Insert(t->right, x);
/* 右子树的高度已经比左子树高2了,即需要右旋 */
if(getheight(t->left) - getheight(t->right) == -2)
{
if(x > t->right->data)/* 右子树的右子树,即右单旋 */
t = sr(t);
else /* 右子树的左子树,即右左双旋 */
t = drl(t);
}
}
/* 最后,插入元素就需要更新树高 */
t->height = max(getheight(t->left), getheight(t->right)) + 1;
return t;
}
void preorder(avl t)
{
if(!t) return ;
printf("BF(%d) = %d\n", t->data, getheight(t->left) - getheight(t->right));
preorder(t->left);
preorder(t->right);
}
int main()
{
avl t = NULL;
int n, i, x;
scanf("%d", &n);
for(i=0; i<n; i++)
{
scanf("%d", &x);
t = Insert(t, x);
}
preorder(t);
return 0;
}