您的位置:首页 > 其它

avl树的c实现

2015-10-15 21:48 323 查看
一.简介:一看到AVL树,首先联想到岛国动作片有木有

,不过AVL树是得名于它的发明者 G.M. Adelson-Velsky 和 E.M. Landis。

AVL树其实是平衡二叉树的限定版,

二.性质

(1).不仅仅具有平衡二叉树的所有性质(注:平衡二叉树性质:左节点的值<父节点的值<右节点的值)

(2).每个节点左右子树高度的差最多为1。这是什么意思呢?给个例子大家就明白了

如:           (a)                                         (b)

                     5                                                 5

                  /      \                                            /     \

                2       8                                        2        8

              /   \      /                                        /   \

            1    4   7                                      1     4

           /                                                   /

         3                                                  3

两者的区别显而易见,(b)树的节点要少一个7。

对于(a)树:5节点的左子树高度为3,右子树的高度为2.相差为1。     

                     2节点的左子树高度为2,右子树的高度为1.相差为1

                     8节点的左子树高度为1,右子树的高度为0.相差为1.

                     以此类推所有节点的左右子树的高度差都小于等于1.

所有(a)树是avl树

对于(b)树:明显5节点左子树高度为3,右子树高度为1,相差为2>1

所以不是avl树

其实判断是avl树比较麻烦,因为每个节点都要看,不过判断不是的话就比较简单了,比较容易可以看出,不过这些都是浮云啦,

通过下面的程序,大家就会知道电脑完全可以帮助我们作出判断。

三.AVL树的作用:

还是举个栗子~

   1

     \

      2

        \

         3

           \

            4

是不是很不美观,完全没有了树的美感~~,但关键是增加了时间的复杂度,而我AVL大法就不会发生这样的事情了

AVL解决这个问题就是用了一个办法:旋转!

四。旋转:

1.单选转:



左边为调整前得节点,我们可以看出k2的左右子树已不再满足AVL平衡条件,调整后的为右图。

细的就不讲了一切尽在注释中

//左单选转,K2是根节点,通过旋转K1成为根节点,K2成为K1的右子树
static Position SingleRotatewithLeft(Position K2)
{
Position K1;
//K1为K2的左子树
K1=K2->Left;
 //K1的右子树成为K2的左子树
K2->Left=K1->Right;
 //K2成为K1的右子树。
//K1成为根,K1的右子树不变,左子树是K2。
//K2的左子树不变,右子树是K1的左子树
K1->Right=K2;
return K1;
}


2.双旋转:

这种用单旋转显然不可以:



                                                         (1)



                                                                            (2)

这里我们将图(1)的Y子树看成如图(2),以k2为子树根节点的树,我们将其子树分成比D低,这里我我先对k3的左子树进行一次情形四的右旋转,然后在进行一次情形1的左旋转

//左双旋转,左边深度大,通过一次右单旋转,一次左单选转
static Position DoubleRotatewithLeft(Position P3)
{
P3->Left=SingleRotatewithRight(P3->Left);
return P3=SingleRotatewithLeft(P3);
}
其实很简单的有木有,看似复杂的东西代码不过5行

废话不多说了,直接撸代码,嘻嘻~~:

                        10

                        /    \

                     5       13 

                   /   \

                 1     7

                       /   \

                     6     8 

#include<stdio.h>
#include<stdlib.h>
//进行求高度时需要
#define Max(a,b) ((a>b)?a:b)
typedef int ElementType;
typedef struct AvlNode
{
ElementType Element;
struct AvlNode * Left;
struct AvlNode * Right;
int height;//高度
}*Position,Node;
static int Height(Position P)
{
if(P==NULL)
return -1;
else
return 1+Max(Height(P->Left),Height(P->Right));
}
//左单选转,P2是根节点,通过旋转P1成为根节点,P2成为P1的右子树
static Position SingleRotatewithLeft(Position P2)
{
Position P1;
//让P1为P2的左子树
P1=P2->Left;
//P1的右子树成为P2的左子树
P2->Left=P1->Right;
//P2成为P1的右子树。
//P1成为根,P1的右子树不变,左子树是P2。
//P2的左子树不变,右子树是P1的左子树
P1->Right=P2;
P2->height=Max(Height(P2->Left),Height(P2->Right))+1;
P1->height=Max(Height(P1->Left),Height(P1->Right))+1;
return P1;
}
static Position SingleRotatewithRight(Position P2)
{
Position P1;
P1=P2->Right;
P2->Right=P1->Left;
P1->Left=P2;
P2->height=Max(Height(P2->Left),Height(P2->Right))+1;
P1->height=Max(Height(P1->Left),Height(P1->Right))+1;
return P1;
}
//左双旋转,左边深度大,通过一次右单旋转,一次左单选转 static Position DoubleRotatewithLeft(Position P3) { P3->Left=SingleRotatewithRight(P3->Left); return P3=SingleRotatewithLeft(P3); }
static Position DoubleRotatewithRight(Position P3)
{
P3->Right=SingleRotatewithLeft(P3->Right);
return P3=SingleRotatewithRight(P3);
}
//插入Element为X的节点
Position Insert(ElementType X,Position *root)
{
//当遍历到节点为NULL时,为该节点申请内存并赋值X
if(*root==NULL){
*root=(Position)malloc(sizeof(struct AvlNode *));
//增强健壮性
if(*root==NULL)
printf("out of space!");
else{
(*root)->Element=X;
(*root)->Left=NULL;
(*root)->Right=NULL;
}
}
//如果X比当前节点的值要大,那么就向右递归遍历
else if(X>(*root)->Element){
(*root)->Right=Insert(X,&(*root)->Right);
//这时已经找到节点并插入,需要做的就是判断是否进行旋转
//当*root的右子树的高度比左子树高度大2时说明需要旋转
if(Height((*root)->Right)-Height((*root)->Left)==2){
//如果插入节点大于根的右儿子的值,说明右边要深,所以进行单旋转
//否则说明右节点的左子树要深,所以双旋转
if((*root)->Right->Element>X)
*root=DoubleRotatewithRight(*root);
else
*root=SingleRotatewithLeft(*root);
}
}
else if(X<(*root)->Element){
(*root)->Left=Insert(X,&(*root)->Left);
if(Height((*root)->Left)-Height((*root)->Right)==2){
if((*root)->Left->Element<X)
*root=DoubleRotatewithLeft(*root);
else
*root=SingleRotatewithRight(*root);
}
}
(*root)->height=Max(Height((*root)->Left),Height((*root)->Right))+1;
return (*root);
}
//递归输入树,如果是0就相当于为空节点
void Input(Position *P)
{
int i;
scanf("%d",&i);
if(i!=0){
*P=malloc(sizeof(struct AvlNode *));
(*P)->Element=i;
Input(&(*P)->Left);
Input(&(*P)->Right);
}
else
(*P)=NULL;
}
//递归输出
void Print(Position root)
{
if(root){
printf("%5d",root->Element);
Print(root->Left);
Print(root->Right);
}
}
int main()
{
int i;
Position root=NULL;
printf("输入树:\n");
Input(&root);
printf("\n");
printf("输出树:\n");
Print(root);
printf("\n");
printf("Input");
scanf("%d",&i);
root=Insert(i,&root);
Print(root);
return 0;
}
把上面的树实现一下:开始可以先不写入6,把6当作插入,结果如下图



愉快撸码,快乐人生。

今天的教程就到这里了,大家学会了吗?

有什么建议欢迎在下方留言哦!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: