查找-之平衡二叉树AVL和红黑树

简介: 二叉排序树的存在的不足是插入新结点导致树不平衡,不平衡树使得查找性能下降

在上一篇文章查找-之二叉排序树(查找、插入、删除)引出的问题是:

二叉排序树的存在的不足是插入新结点导致树不平衡,不平衡树使得查找性能下降

解决方法

构建平衡的二叉树

AVL树、红黑树

AVL树:带有严格平衡条件的二叉查找树,用平衡因子差值判断树是否平衡并通过旋转实现平衡,左右子树的高度不超过1

适用于:插入和删除次数较少(插入删除操作使得树失去平衡,为维持严格平衡旋转操作,使得性能下降),查找次数较多的情况


  Windows NT内核中广泛存在

算法复杂度:时间复杂度,查找O(logn),  插入和删除 O(logn)

存在的问题

AVL树的每个结点只能存放一个元素、并且每个结点只有两个子结点,查找时需要多次磁盘IO操作,每次查询从磁盘中的一页数据拷贝到内存,其中树的每一层结点存放在一页中,不同层数据存放在不同页,为此查找数据需要多次操作磁盘IO


红黑树:弱平衡二叉查找树,通过对任何一条根到叶子路径各个结点着色来限制,确保没有一条路径会比其它路径长出两倍

适用于:插入、删除次数较多,查找次数较多的情况

    应用:


       linux进程调度用红黑树管理进程控制块


      Java Treemap、TreeSet


      C++ STL


      Nginx 中的定时器管理

算法复杂度:

查找、删除、插入操作的时间复杂度:O(logn),  O(log2 (n))

统计性能好于AVL树

AVL树:

1)左子树高于右子树,则旋转因子BF为正,该结点对应的整棵树右旋

2)右子树高于左子树,则旋转因子BF为负,该结点对应的整棵树左旋

3)符号不统一,双旋

//二叉树的二叉链表结点结构定义
typedef struct BiTNode
{
    int data;                                    //结点数据
    int bf;                                       //结点平衡因子
    struct BiTNode *lchild,*rchild;//左右孩子指针
} BiTNode,*BiTree;
//对以P为根的二叉排序树做右旋处理
void R_Rotate(BiTree *P)
{
   BiTree L;
   L=(*P)->lchild;
   (*P)->lchild=L->rchild;
   L->rchild=(*P);
   *P=L;//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所指结点为根的二叉树作左平衡旋转处理,左子树的高度大于右子树的高度
void LeftBalance(BiTree *T)
{
     BiTree L,Lr;
     L=(*T)->lchild;//L指向左子树的根结点
     switch(L->bf)
     {
        //检查左子树的平衡度,并做相应的平衡处理
         case LH://+1  说明L->bf和根结点的符号相同,  (新结点插在T的左孩子的左子树上)----右旋处理
             (*T)->bf=L->bf=EH;
            R_Rotate(T);
            break;
        case RH:// -1 说明L->bf  -1和根结点的符号相反 ,  (新结点插入在T的左孩子的右子树上) -----双旋处理
            Lr=L->rchild;//Lr指向T的左孩子的右子树根
            switch(Lr->bf)//T的左孩子的右子树根做判断---修改T及T的左孩子L的平衡因子  ??
               { 
                   case LH: //左高 +1
                            (*T)->bf=RH;//-1
                            L->bf=EH;//0
                             break;
                  case EH://等高 0
                            (*T)->bf=L->bf=EH;//0
                            break;
                  case RH: //右高 -1
                            (*T)->bf=EH;//0
                            L->bf=LH;//+1
                            break;
              }
            Lr->bf=EH;//0
            L_Rotate(&(*T)->lchild);//T的左孩子左旋
            R_Rotate(T);//T右旋
    }
}
// 若在平衡二叉排序树T中不存在和e有相同关键字的结点,则插入一个
//因插入数据而使得二叉排序树失去平衡,则做平衡旋转处理
//taller 反映树T长高与否
Status InsertAVL(BiTree *T,int e,Status *taller)
{
       if(!*T)//如果树空
       {  
           *T=(BiTree)malloc(sizeof(BiTNode));
           (*T)->data=e;
           (*T)->lchild=(*T)->rchild=NULL;
           (*T)->bf=EH;
           *taller=True;
      }
     else
      {
          if(e==(*T)->data)//树中已存在和e有相同关键字的结点--则不再插入
              {
                  *taller=False;
                   return False;
              }
          if(e<(*T)->data)//待插入的数小于根节点  ---在T的左子树搜索
          {
             //继续在T的左子树中进行搜索
              if(!InsertAVL(&(*T)->lchild,e,taller))//若在左子树中找到该插入的结点,则返回false
                     return False;
              if(taller)//已插入到T的左子树且左子树长高
               {
                switch((*T)->bf)//检查T的平衡度
                    {
                          case LH://原本左子树比右子树高,现在左子树插入,需做左平衡处理
                                 LeftBalance(T);
                                 *taller=False;
                                 break;
                          case EH://原本左子树等于右子树,现在左子树插入,现在左子树增高
                                 (*T)-bf=LH;
                                 *taller=True;
                                 break;
                         case RH://原本右子树比左子树高,现在左子树插入,现在左右子树等高
                                 (*T)->bf=EH;
                                 *taller=False;
                                 break;
                    }
              }
          }
        else//继续在T的右子树中进行搜索
          {
             if(!InsertAVL(&(*T)->rchild,e,taller)) //若在右子树中找到该插入的结点,则返回false ,未插入
                  {
                       return False;
                } 
             if(*taller)//插入T的右子树且右子树长高
               {
                   switch((*T)->bf)
                      {
                         case LH:   //原本左子树比右子树高,现在在右子树插入,左右子树等高
                                      (*T)->bf=EH;
                                      *taller=False;
                                      break;
                       case EH:  //原本左右子树等高,现在在右子树插入,右子树增高
                                    (*T)->bf=RH;
                                    *taller=True;
                                    break;
                       case RH:  //原本右子树比左子树高,现在在右子树插入,需要做右平衡处理
                                    RightBalance(T);
                                    *taller=False;
                                    break;
                     }
             }
         }
     }
   return True;
}

构建平衡二叉树的示例

//构建平衡二叉树
int i;
int a[10]={3,2,1,4,5,6,7,10,9,8};
BiTree T=NULL;
Status taller;
for(i=0;i<10;i++)
{
      InsertAVL(&T,a[i],&taller);
}

参考

《大话数据结构》

目录
相关文章
|
11月前
|
人工智能 C++
平衡树--treap
平衡树--treap
54 0
|
11月前
|
算法
AVL树,Treap树,红黑树的实现(上)
AVL树,Treap树,红黑树的实现
二叉搜索树之AVL树
二叉搜索树之AVL树
|
11月前
AVL树,Treap树,红黑树的实现(下)
AVL树,Treap树,红黑树的实现
|
12月前
|
机器学习/深度学习 C++
AVL树的插入(C++实现)
AVL树的插入(C++实现)
66 0
|
存储 算法 关系型数据库
有了二叉树,平衡二叉树为什么还需要红黑树
有了二叉树,平衡二叉树为什么还需要红黑树
92 0
有了二叉树,平衡二叉树为什么还需要红黑树
|
算法
平衡二叉树(AVL树)
平衡二叉树(AVL树)
76 0
数据结构-各种树(二叉树、二叉查找树、平衡二叉树、红黑树、B树、B+树)
数据结构-各种树(二叉树、二叉查找树、平衡二叉树、红黑树、B树、B+树)
|
算法 C++ 容器
【C++】AVL树和红黑树的插入
【C++】AVL树和红黑树的插入
二叉树、平衡二叉树AVL、红黑树、B树、B+树
B树的阶数等于叶节点最大关键字数量+1(因为关键字两边都有指向子节点的指针-分叉) 在m阶(m叉)B树中除根结点外,任何节点至少[m/2]个分叉,即至少[m/2]-1个关键字, [ ]代表向上取整。 节点内的关键字采用顺序查找或二分查找。 因为关键字太少会导致树变高,降低查找效率。另外就是保证同级子树的高度相同-平衡。