查找-之平衡二叉樹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)符號不統一,雙旋

 

 

C僞代碼:

//二叉樹的二叉鏈表結點結構定義
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);

}

參考

《大話數據結構》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章