您的位置:首页 > 其它

AVL树的定义和基本操作

2014-01-15 23:35 405 查看
AVL树是带有平衡的二叉查找树!一颗AVL树的每一个结点的左子树和右子树的深度最多只有1的差距,这就保持了这颗二叉树的平衡!很大程度的提高了树的使用效率!

当我们对树进行一系列操作(插入、删除等)后,AVL树很可能就不能保持AVL的特性,所以在进行操作时,我们必须重新平衡这颗二叉树!

我们把必须重新平衡的结点叫做α(这样可以减少很多文字,又好理解),由于任意结点最多只有两个儿子,因此出现高度不平衡就需要α点的两个子结点高度差大于等于2。

如意看出,这种不平衡可能出现下面四种情况:

1、对α的左子树的左子树进行插入;

2、对α的左子树的右子树进行插入;

3、对α的右子树的右子树进行插入;

4、对α的右子树的左孩子进行插入。

1、3的情况是比较简单的,进行一次旋转就能完成平衡,而2、4的情况就比较复杂了,需要两次旋转!

我先定义一个AVL数的结点结构,只比查找树多一个属性:深度

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/*结点数据域类型定义*/
typedef int ElemType;

/*AVL树的结点声明*/
typedef struct _AVLTNode
{
ElemType data;
struct _AVLTNode * lchild;
struct _AVLTNode * rchild;
int height;		/*深度*/
}AVLTNode,*AVLTree;


还需要一个辅助的函数,帮助获取这个深度

/*获取树的高度*/
static int Height(AVLTree T)
{
if(!T)
{
return -1;
}
else
{
return T->height;
}
}


这时候就对树进行插入操作,即创建二叉树,又可以插入!比较容易,就是几个函数的调用:
/*AVL书中插入结点,插入完毕后树依然保持AVL特性*/
AVLTree AVL_Insert(AVLTree * T, ElemType e)
{
if (*T == NULL)			//如果空树,就构造一颗根结点
{
*T = (AVLTree)malloc(sizeof(AVLTNode));
if(!*T)
{
return NULL;
}

(*T)->data = e;
(*T)->lchild = NULL;
(*T)->rchild = NULL;
(*T)->height = 0;		//初始化深度为0;
}
else if(e < (*T)->data)			//左边
{
(*T)->lchild =AVL_Insert(&(*T)->lchild,e);			//递归左子树插入

if (Height((*T)->lchild) - Height((*T)->rchild) == 2)			//判断左子树和右子树的深度差为2 说明AVL特性被破坏,需要进行平衡
{
if (e < (*T)->lchild->data)				//属于 上面四种情况中的第一个,只需要一次旋转
{
*T = SingleRotateLeft(*T);
}
else
{
*T = DoubleRotateLeft(*T);		//第二个情况 双旋转
}
}
}
else if(e > (*T)->data)			//反之 右面,操作和左面相反而已
{
(*T)->rchild =AVL_Insert(&(*T)->rchild,e);
if (Height((*T)->rchild) - Height((*T)->lchild) == 2)
{
if (e > (*T)->rchild->data)
{
*T = SingleRotateRight(*T);
}
else
{
*T = DoubleRotateRight(*T);
}
}
}

(*T)->height = Max(Height((*T)->rchild),Height((*T)->lchild))+1;		//深度加1
return *T;
}


然后我把四种情况的看书写出来:

/*AVL树左单旋转*/
static AVLTree SingleRotateLeft(AVLTree k1)
{
AVLTree k2;

k2 = k1->lchild;
k1->lchild = k2->rchild;
k2->rchild = k1;

k1->height =  Max(Height(k1->rchild),Height(k1->lchild))+1;
k2->height =  Max(Height(k2->rchild),Height(k2->lchild))+1;
return k2;
}

/*AVL树右单旋转*/
static AVLTree SingleRotateRight(AVLTree k1)
{
AVLTree k2;

k2 = k1->rchild;
k1->rchild = k2->lchild;
k2->lchild = k1;

k1->height =  Max(Height(k1->rchild),Height(k1->lchild))+1;
k2->height =  Max(Height(k2->rchild),Height(k2->lchild))+1;
return k2;
}

/*AVL树左双旋转*/
static AVLTree DoubleRotateLeft(AVLTree k1)
{
k1 = SingleRotateRight(k1->lchild);
return SingleRotateLeft(k1);
}

/*AVL树右双旋转*/
static AVLTree DoubleRotateRight(AVLTree k1)
{
k1 = SingleRotateLeft(k1->rchild);
return SingleRotateRight(k1);
}


其实就是几个指针之间的几个操作,比之前的链表的哪些操作简单多了,初学者最好的方法就是画图,根据图分析这四种情况,一目了然!

这里还需要了一个Max函数,我就不贴出来了,就是比较两个数,返回较大的数。写不出来去小学深造去吧!

我用前序遍历和中序遍历做一下测试,从1打到15,顺序输入,看看输出是什么情况:

int main()
{
AVLTree T = NULL;
int i=0;
for (i=1;i<16;i++)
{
AVL_Insert(&T,i);
}

PreOrderTraverse(T);
printf("\n\n");
InOrderTraverse(T);
getchar();
getchar();
return 0;
}


结果如下:



第一排是前序遍历结果,第二排是中序遍历!!!!!刚好是一颗3层的满二叉树!

我再倒着15到1输入看看结果是不是一样:

int main()
{
AVLTree T = NULL;
int i=0;
for (i=15;i>=1;i--)
{
AVL_Insert(&T,i);
}

PreOrderTraverse(T);
printf("\n\n");
InOrderTraverse(T);
getchar();
getchar();
return 0;
}
结果一样:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: