平衡二叉树(AVL)

二叉排序树可以完美地生成一个二叉树,但是会有一种极端的情况。就是会生成一棵斜树,这种情况下,树的深度很大,会和节点数一样大。那么查找一个元素的算法复杂度达到O(n)。这种效率比较低,如果生成一棵类似于一棵完全二叉树,则树的深度为[log n]+1。那么查找的复杂度就将为O(log n)了。

这里怎么生成这个平衡二叉树呢?这就需要在每次插入节点的时候先要对树形进行判断,看是否破坏了树的结构。

树形的判断依据又是什么呢?

我们首先需要对平衡二叉树进行一个定义,所谓的平衡二叉树就是属于二叉排序树的一种,每个节点的左子树与右子树的高度差的最大值为1.

为了形象地描述这一点,这里引入一个概念,平衡因子(Balance Factor),就是二叉树节点上左子树的深度减去右子树的深度,作为平衡二叉树,这个平衡因子的取值只有-1,0,1。接下来我们定义平衡二叉树的数据结构,写入avl.h中:

#ifndef _BITREE_AVL
#define _BITREE_AVL

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<ctype.h>
#include<time.h>

typedef struct BiTNode{
	int data;
	int bf;
	struct BiTNode *lchild,*rchild;
}BiTNode,*Bitree;
#define max(a,b)  a>b?a:b
#endif

函数定义,function.h。

#ifndef AVL_FUNC_
#define AVL_FUNC_
/*Average Binary Tree*/
#define LH +1
#define EH 0
#define RH -1

void R_Rotate(Bitree *P);
void L_Rotate(Bitree *P);
void LeftBalance(Bitree *P);
void RightBalance(Bitree* T);
int insertAVL(Bitree *T,int e,int *taller);
int DeleteAVL(Bitree *T, int key, int &shorter);
int search(Bitree *P,int e);
int getHeight(Bitree P);
#endif

插入节点后,发现二叉树不平衡了,但是怎么调整呢?不可能是考虑怎么调整整个二叉树。这里只需调整这局部不平衡树,消除局部不平衡因子,整个二叉树也就平衡了。找出这个局部不平衡树,就是最小不平衡子树。就是距离插入节点最近,平衡因子的绝对值大于1的节点为根的子树。

我们队最小不平衡子树的操作有两种,分别是左旋与右旋。

这里先考虑最简单的情况,新插入的节点均在左子树或者右子树一边。这种情况下,平衡因子的符号都一致,要么都正,要么都负,在左子树的就做右旋操作,在右子树的就做左旋操作。写入algorithm.c文件中,其左右旋的实现代码如下:

void R_Rotate(Bitree *P)
{
	Bitree L;
	L=(*P)->lchild;
	(*P)->lchild=L->rchild;
	L->rchild=(*P);
	*P=L;
}
void L_Rotate(Bitree *P)
{
	Bitree R;
	R=(*P)->rchild;
	(*P)->rchild=R->lchild;
	R->lchild=(*P);
	*P=R;
}

对于上面的代码可以这样理解,从插入节点到最小不平衡节点,这里称和插入节点相连的节点为旋转节点,最小不平衡节点为根。

左旋   带    旋转节点的左子树给最小不平衡节点的右子树,旋转节点成为新的根节点,最小不平衡节点成为左子树;

右旋   带   旋转节点的右子树给最小不平衡节点的左子树,旋转节点成为新的根节点,最小不平衡节点成为右子树。

考虑完最简单的情况,让我们考虑下复杂点儿的,世界上的事情啊。或许就是这个样子,就如前几天的高考的那个学霸说的那样“爱情像英语这么简单就好了~”。

咱们进入正题,这次的情况是这样的,插入的节点是最小不平衡节点的左子树的右子树,这种情况下,出现了平衡因子有正有负的情况。这种情况下就不能是简单的左旋或者右旋了。首先得调整平衡因子,使其符号一致。首先查看最小不平衡节点的左子树的右子树这种情况,先以最小不平衡节点的左子树为根,以插入节点为旋转节点,这样是在右侧,进行左旋,将所有节点转到左侧,然后平衡因子一致。然后得到新的最小不平衡树,就和上面提到的最简单的情况一致,进行右旋即可使二叉树平衡。然后最小不平衡节点的右子树的左子树,这种情况与第一种对称,先右旋然后再左旋。

有了上面的思路,就有了下面的代码,添加下面的代码到algorithm.c中:

void LeftBalance(Bitree *P)
{
	Bitree L,Lr;
	L=(*P)->lchild;
	switch(L->bf)
	{
	case LH:
		(*P)->bf=L->bf=EH;
		R_Rotate(P);
		break;
	case RH:
		Lr=L->rchild;
		switch(Lr->bf){
	        case LH:
			   (*P)->bf=RH;
			    L->bf=EH;
				break;
		   case EH:
			   (*P)->bf=L->bf=EH;
			   break;
		   case RH:
			   (*P)->bf=EH;
			   L->bf=LH;
			   break;
		}
		Lr->bf=EH;
		L_Rotate(&(*P)->lchild);
		R_Rotate(P);
	}
}

void RightBalance(Bitree* P)  
{  
    Bitree L,Ll;  
    L= (*P)->rchild;  
    switch (L->bf)  
    {  
    case RH:  
        (*P)->bf = L->bf = EH;  
        L_Rotate(P);  
        break;  
    case LH:  
        Ll = L->lchild;  
        switch(Ll->bf)  
        {  
        case LH:  
            (*P)->bf = EH;  
            L->bf = RH;  
            break;  
        case EH:  
            (*P)->bf = L->bf = EH;  
            break;  
        case RH:  
            (*P)->bf = EH;  
            L->bf = LH;  
            break;  
        }  
        Ll->bf = EH;  
        R_Rotate(&(*P)->rchild);  
        L_Rotate(P);  
        break;  
    }  
}  

有了上面的这些调整二叉树的操作,我们就可以生成一棵平衡二叉树了:

int insertAVL(Bitree *T,int e,int *taller)
{
	if(!*T)
	{
		*T=(Bitree)malloc(sizeof(BiTNode));
		(*T)->data=e;
		(*T)->lchild=(*T)->rchild=NULL;
		(*T)->bf=EH;
		*taller=1;
	}
	else
	{
		if(e==(*T)->data)
		{
			*taller=0;
			return 0;
		}
		if(e<(*T)->data)
		{
			if(!insertAVL(&(*T)->lchild,e,taller))
				return 0;
			if(*taller)//左树增高
			{
				switch((*T)->bf)
				{
				case LH://第二次出现增高,则调整树的形状
					LeftBalance(T);
					*taller=0;
					break;
				case EH:
					(*T)->bf=LH;
					*taller=1;
					break;
				case RH:
					(*T)->bf=EH;
					*taller=0;
					break;
				}
			}
		}
		else
		{
			if(!insertAVL(&(*T)->rchild,e,taller))
				return 0;
			if(*taller)
			{
				switch((*T)->bf){
				case LH:
					(*T)->bf=EH;
					*taller=0;
					break;
				case EH:
					(*T)->bf=RH;
					*taller=1;
					break;
				case RH:  //出现两次右树增高,则调整树形状
					RightBalance(T);
					*taller=0;
					break;
				}
			}
		}
	}
	return 1;
}

对平衡二叉树的操作,搜索和删除:

int search(Bitree *P,int e)
{
	if(!*P)
		return 0;
	if((*P)->data==e)
		return 1;
	else if((*P)->data<e)
		return search(&(*P)->lchild,e);
	else
		return search(&(*P)->rchild,e);
}

int DeleteAVL(Bitree *T, int key, int &shorter){  
    if (!T)  
    { 
        shorter = false;  
        return 0;  
    }  
    else  
    {  
        if(key == (*T)->data)  
        {  
           Bitree q = *T;  
            if (!(*T)->lchild)  
            {  
                q = *T;  
                *T = (*T)->rchild;  
                free(q);  
                shorter = 1;  
                return 1;  
            }  
            else if (!(*T)->rchild){
                q = *T;  
                *T= (*T)->lchild;
                free(q);  
                shorter = 1;  
                return 1;  
            }  
            else {    
                Bitree s = (*T)->lchild;  
                while(s->rchild)  
                   s = s->rchild;

                (*T)->data = s->data;  
                key = s->data; 
            }  
        }  
        if (key < (*T)->data){  
            if (!DeleteAVL(&(*T)->lchild, key, shorter)) 
				return 0;  
            if (shorter){  
                switch((*T)->bf){  
                case LH:
					(*T)->bf = EH; 
					shorter = 1;
					break;  
                case EH:
					(*T)->bf = RH;
					shorter = 0;
					break;  
                case RH:
			RightBalance(T);   
                    if ((*T)->rchild->bf == EH)  
                        shorter = 0;  
                    else   
                        shorter = 1;
					break;  
                }  
            }  
        }  
        else{  
            if (!DeleteAVL(&(*T)->rchild, key, shorter)) 
				return 0;  
            if (shorter){  
                switch((*T)->bf){  
                case LH:
					LeftBalance(T);  
                    if ((*T)->lchild->bf == EH)  
                        shorter = 0;  
                    else   
                        shorter = 1;
					break;  
                case EH:
					(*T)->bf = LH; 
					shorter = 0;
					break;  
                case RH:
					(*T)->bf = EH;
					shorter = 1;
					break;  
                }  
            }  
        }  
    }  
    return 1;  
}

然后测试平衡二叉树的生成,并进行遍历。

#include"avl.h"
#include"function.h"

#define DEBUGPRINT  printf("file_name:%s;\ncurrent_line:%d;\ncompile_time:%s:%s;\n\nprogram start...:\n",__FILE__,__LINE__,__DATE__,__TIME__)
void midordertraverse(Bitree T)
{
      if(T==NULL)
                 return ;
      midordertraverse(T->lchild);
    printf("%d\t",T->data);
      midordertraverse(T->rchild);
}
int main()
{
	DEBUGPRINT;
	Bitree AVL=NULL;
	int taller;
	srand(time(0));
	printf("please input treenode number:\n");
        int n,i;
	scanf("%d",&n);
	for(i=0;i<=n;i++){
		insertAVL(&AVL,rand()%1000,&taller);
	}
	midordertraverse(AVL);
	printf("\n");
}

然后我们对生成的两棵树的深度进行对比,节点为20的二叉排序树与二叉平衡树的深度结果如下:

《平衡二叉树(AVL)》

    原文作者:平衡二叉树
    原文地址: https://blog.csdn.net/xygl2009/article/details/46439131
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞