平衡二叉树研究(AVL树)

本文详细介绍了平衡二叉树的概念、实现原理及代码解析,包括平衡因子、最小不平衡子树的定义与处理方法,以及插入、删除操作的具体实现。通过实际代码演示,深入理解平衡二叉树在保持树平衡方面的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近在研究数据结构,使用教材是程杰的《大话数据结构》,这本树写的非常好,看到了平衡二叉树,对该章研究了下,敲了下代码,并通过加断点调试,学习了不少,记录下来,供大家参考。

(1)平衡二叉树(AVL树概念)

一种二叉排序树,其中每一个结点的左子树和右子树的高度差的绝对值小于等于1。

平衡因子BF:左子树深度减去右子树深度的值(有正有负)

最小不平衡树:距离插入结点最近的,且平衡因子的绝对值大于1的结点为根的子树,称为最小不平衡子树。

(2)平衡二叉树实现原理

基本思想:当插入一个新的结点,出现不平衡情况时,就对最小不平衡子树进行调整。

调整平衡时主要是两种情况:

(a)最小不平衡子树,根结点平衡因子与左或者右子树根节点平衡因子符号同号,直接进行左旋或者右旋即可。

(b)最小不平衡子树,根结点平衡因子与左或者右子树根节点平衡因子符号异号,先对最小不平衡子树根节点的左右子树根节点进行旋转,使两者平衡因子符号由异号变为同号,然后对最小不平衡子树根节点进行旋转(其实就是进行两次旋转,双旋处理)。

(3)平衡二叉树实现

读代码的一点感悟:

(a)插入结点函数InsertAVL是一个递归插入操作,当新增一个结点成功后,然后退出当前的插入操作,递归回退到上一次插入操作,并对新插入结点的上一次结点的平衡因子值进行修改。

(b)直接阅读代码有些困难,最好就是在编译器中加断点进行调试,这样对于理解插入结点时,如何调整平衡的原理很有用。(我是这样的情况,纸上代码看了很久都没看懂,上机调试了一把,立马恍然大悟)

(c)结点删除时,由插入结点是递归,可以推测出删除时也用递归,可见递归操作很强大啊!二叉排序树删除结点三种情况:(a1)叶子结点(a2)仅有左或者右子树(a3)左右子树都存在。删除结点时,判断树的高度是否变矮。(a1)(a2)情况,树高度变矮了,(a3)情况,找到直接前驱,方法是"先向左,然后向右到尽头",将直接前驱的值付给根节点,然后在根节点左子树中递归删除直接前驱结点。

二叉链表结点结构体定义:

typedef struct BiTNode          //结点结构体
{
   int data;
   int bf;                      //结点平衡因子
   struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;
右旋操作:

/*********************************************
**功能:对以p为根的二叉排序树做右旋处理
**p:二叉排序树
*********************************************/
void R_Rotate(BiTree *p)
{
   BiTree L;
   L = (*p)->lchild;               //L指向根节点的左子树
   (*p)->lchild = L->rchild;       //L的右子树挂在根节点的左子树上
   L->rchild = (*p);               
   (*p) = L;                        //p指向新的根节点
}
左旋操作:

/*********************************************
**功能:对以p为根的二叉排序树做左旋处理
**p:二叉排序树
*********************************************/
void L_Rotate(BiTree *p)
{
   BiTree R;
   R = (*p)->rchild;
   (*p)->rchild = R->lchild;
   R->lchild = (*p);
   (*p) = R;
}
左平衡旋转处理:

#define LH 1            //左高
#define EH 0            //等高
#define RH -1           //右高

/*********************************************
**功能:对指针T所指结点为根的结点进行左平衡旋转处理
**T:二叉排序树
*********************************************/
void LeftBalance(BiTree *T)
{
   BiTree L,Lr;
   L = (*T)->lchild;               //L指向左子树根节点
   switch(L->bf)
   {
   case LH:                        //新节点插入在左孩子的左子树上,要作单右旋处理
      (*T)->bf = L->bf = EH;
      R_Rotate(T);
      break;
   case  RH:
      Lr = L->rchild;
      switch(Lr->bf)
      {
      case LH:
         (*T)->bf = RH;
         L->bf = EH;
         break;
      case EH:
         (*T)->bf=L->bf=EH;
         break;
      case RH:
         (*T)->bf = EH;
         L->bf = LH;
         break;
      }
      Lr->bf = EH;
      L_Rotate(&(*T)->lchild);           //对T的左子树做左旋处理
      R_Rotate(T);                       //对T做右旋处理
   }
}
右平衡旋转处理:

/*********************************************
**功能:对指针T所指结点为根的结点进行右平衡旋转处理
**T:二叉排序树
*********************************************/
void RightBalance(BiTree *T)
{
   BiTree R,Rl;
   R = (*T)->rchild;
   switch(R->bf)
   {
   case RH:                 //此时,根节点和右子树结点平衡因子相同,直接对根节点左旋
      (*T)->bf = R->bf = EH;
      L_Rotate(T);
      break;
   case LH:
      Rl = R->lchild;       //指向右子树的根节点的左子树,异号,需要进行双旋处理
      switch(Rl->bf)
      {
      case RH:
         (*T)->bf = LH;
         R->bf = EH;
         break;
      case EH:
         (*T)->bf = R->bf=EH;
         break;
      case LH:(*T)->bf = EH;
         R->bf = RH;
         break;
      }
      Rl->bf = EH;
      R_Rotate(&(*T)->rchild);           //对T的右子树根节点做右旋操作
      L_Rotate(T);                       //对根节点T做左旋操作
   }
}
插入结点主函数:

/*********************************************
**功能:平衡二叉树中插入一个结点e,若e不存在,插入并返回true,否则返回false
**T:平衡二叉树
**e:带插入结点
**taller:树是否增高
*********************************************/
int InsertAVL(BiTree *T,int e,int *taller)
{
   if(!*T)
   {
      *T =(BiTree)malloc(sizeof(BiTNode));
      (*T)->data = e;
      (*T)->lchild = NULL;
      (*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)                         //检查T的平衡度
            {
            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;
}
打印二叉排序树:

/*********************************************
**功能:递归打印二叉树,中序遍历
**T:二叉排序树
*********************************************/
void PrintBTree(BiTree T)
{
   if (!T)                    //递归出口
      return;
   PrintBTree(T->lchild);
   printf("%d\t",T->data);
   PrintBTree(T->rchild);
}


删除结点函数:

/*********************************************
**功能:平衡二叉树中进行删除,又该节点,删除并返回true,若没有该节点,返回false
**T:平衡二叉树
**e:删除的结点键值
**shorter:树的高度是否降低
*********************************************/
int DeleteAVL(BiTree *T,int e,int* shorter)
{
   //提示:插入结点使用的递归插入,可以联想到删除也是用的递归删除
   if (!*T)                 //不存在该节点
   {
      return 0;
   }
   else if ((*T)->data == e)      //找到该节点
   {
      BiTree q;
      if (!(*T)->lchild)          //如果左子树为空,只需要重接右子树
      {
         q = (*T);
         (*T) = (q->rchild);
         free(q);
         *shorter = 1;
      }
      else if (!(*T)->rchild)    //右子树为空,只需要重接左子树
      {
         q = (*T);
         (*T) = q->lchild;
         free(q);
         *shorter = 1;
      }
      else                       //左右子树都不为空
      {
         q = (*T)->lchild;       //先向左,然后向右到尽头
         while(q->rchild)
         {
            q = q->rchild;
         }
         (*T)->data = q->data;
         DeleteAVL(&(*T)->lchild,q->data,shorter);             //左子树中递归删除前驱结点
      }
   }
   else if (e < (*T)->data)       //左子树中递归查找
   {
      if (!DeleteAVL(&(*T)->lchild,e,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)        //画图看一下,很easy
            {
               *shorter = 0;
            }
            else
            {
               *shorter = 1;
            }
            break;
         }
      }
   }
   else                          //右子树中递归查找
   {
      if (!DeleteAVL(&(*T)->rchild,e,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 0;
}


测试代码主函数:

int _tmain(int argc, _TCHAR* argv[])
{
   int a[] = {3,2,1,4,5,6,7,10,9,8};
   BiTree T = NULL;
   int taller;
   for (int i=0;i<10;++i)
   {
      InsertAVL(&T,a[i],&taller);
   }
   PrintBTree(T);
   int shorter;
   DeleteAVL(&T,10,&shorter);
   PrintBTree(T);
   printf("\n");

   return 0;


打印结果:










评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值