數據結構-平衡二叉樹 旋轉過程平衡因子分析 c和java代碼實現對比

平衡二叉搜索樹(Self-balancing binary search tree)又被稱爲AVL樹(有別於AVL算法),且具備如下性質:它是一 棵空樹或它的左右兩個子樹的高度差的絕對值不超過1,而且左右兩個子樹都是一棵平衡二叉樹,同時,平衡二叉樹一定是二叉排序樹。java

高度差能夠用平衡因子bf來定義,咱們用左子樹的高度減去右子樹的高度來表示bf,即-1<|bf|<1。node

引入平衡二叉樹是因爲二叉排序樹,在某些狀況會致使樹的高度一直的增長,好比一組有序的數據,在查找或建立時遞歸層級會很深,致使方法棧容易溢出。算法

平衡二叉樹是經過旋轉來緩解樹高度增長過快的狀況。數據結構

先介紹下最小不平衡節點的概念:插入一個節點以後,距離這個插入節點最近的不平衡節點就是最小不平衡節點。就是說在遞歸插入節點後,開始回溯,碰到的第一個不平衡的節點就是最小不平衡節點。函數

當最小不平衡節點右子樹高則須要左旋,左子樹高則須要右旋(還有些狀況須要先對其左/右子樹旋轉)。this

思考:spa

一、既然旋轉是經過平衡因子|bf|>1來決定怎麼旋轉的,那麼在旋轉前這些平衡因子是何時賦值的呢?指針

二、旋轉以後,哪些節點須要調整?,平衡因子又改如何調整呢?code

 

下圖只列出左子樹高的幾種狀況,T表示最小不平衡節點,L表示其左子樹,LR表示L的右子樹,blog

爲了形象用EH(0),LH(1),RH(-1)分別表示某一節點 左右子樹相等、左子樹高、右子樹高三種狀況。

根據L節點的左右子樹高度差來肯定直接右旋仍是先左旋再右旋,由於L爲最小不平衡子樹的左子樹,故不會出現L.bf=EH的狀況。

1、L.bf=LH

右旋:

旋轉以後T.bf=L.bf=EH

2、L.bf=RH

先左旋再右旋:

當L的平衡因子爲-1時則須要先對L進行右旋,而後再對T進行左旋。根據LR的狀況再分爲下面三種(由於旋轉兩次,那麼最後最小不平衡子樹的根節點爲LR,而且LR.bf=EH

     一、 LR=EH

旋轉以後T.bf=L.bf=EH

   二、 LR=LH

    

旋轉以後T.bf=RH, L.bf=EH

   三、 LR=RH

   

旋轉以後T.bf=EH, L.bf=LH 

我認爲網上最容易懂的C語言版代碼入下:

    1. #include "stdio.h"      
    2. #include "stdlib.h"     
    3. #include "io.h"    
    4. #include "math.h"    
    5. #include "time.h"  
    6. 7. #define OK 1  
    8. #define ERROR 0  
    9. #define TRUE 1  
    10. #define FALSE 0  
    11. #define MAXSIZE 100 /* 存儲空間初始分配量 */  
    12. 13. typedef int Status; /* Status是函數的類型,其值是函數結果狀態代碼,如OK等 */   
    14. 15. 16. /* 二叉樹的二叉鏈表結點結構定義 */  
    17. typedef  struct BiTNode /* 結點結構 */  
    18. { 19.     int data;   /* 結點數據 */  
    20.     int bf; /* 結點的平衡因子 */   
    21.     struct BiTNode *lchild, *rchild;    /* 左右孩子指針 */  
    22. } BiTNode, *BiTree; 23. 24. 25. /* 對以p爲根的二叉排序樹做右旋處理, */  
    26. /* 處理以後p指向新的樹根結點,即旋轉處理以前的左子樹的根結點 */  
    27. void R_Rotate(BiTree *P) 28. { 29. BiTree L; 30.     L=(*P)->lchild; /* L指向P的左子樹根結點 */   
    31.     (*P)->lchild=L->rchild; /* L的右子樹掛接爲P的左子樹 */   
    32.     L->rchild=(*P); 33.     *P=L; /* P指向新的根結點 */   
    34. } 35. 36. /* 對以P爲根的二叉排序樹做左旋處理, */  
    37. /* 處理以後P指向新的樹根結點,即旋轉處理以前的右子樹的根結點0 */  
    38. void L_Rotate(BiTree *P) 39. { 40. BiTree R; 41.     R=(*P)->rchild; /* R指向P的右子樹根結點 */   
    42.     (*P)->rchild=R->lchild; /* R的左子樹掛接爲P的右子樹 */   
    43.     R->lchild=(*P); 44.     *P=R; /* P指向新的根結點 */   
    45. } 46. 47. #define LH +1 /*  左高 */   
    48. #define EH 0  /*  等高 */   
    49. #define RH -1 /*  右高 */   
    50. 51. /* 對以指針T所指結點爲根的二叉樹做左平衡旋轉處理 */  
    52. /* 本算法結束時,指針T指向新的根結點 */  
    53. void LeftBalance(BiTree *T) 54. { 55. BiTree L,Lr; 56.     L=(*T)->lchild; /* L指向T的左子樹根結點 */   
    57.     switch(L->bf) 58.     { /* 檢查T的左子樹的平衡度,並做相應平衡處理 */   
    59.          case LH: /* 新結點插入在T的左孩子的左子樹上,要做單右旋處理 */   
    60.             (*T)->bf=L->bf=EH; 61. R_Rotate(T); 62.             break; 63.          case RH: /* 新結點插入在T的左孩子的右子樹上,要做雙旋處理 */   
    64.             Lr=L->rchild; /* Lr指向T的左孩子的右子樹根 */   
    65.             switch(Lr->bf) 66.             { /* 修改T及其左孩子的平衡因子 */   
    67.                 case LH: (*T)->bf=RH; 68.                          L->bf=EH; 69.                          break; 70.                 case EH: (*T)->bf=L->bf=EH; 71.                          break; 72.                 case RH: (*T)->bf=EH; 73.                          L->bf=LH; 74.                          break; 75. } 76.             Lr->bf=EH; 77.             L_Rotate(&(*T)->lchild); /* 對T的左子樹做左旋平衡處理 */   
    78.             R_Rotate(T); /* 對T做右旋平衡處理 */   
    79. } 80. } 81. 82. /* 對以指針T所指結點爲根的二叉樹做右平衡旋轉處理, */   
    83. /* 本算法結束時,指針T指向新的根結點 */   
    84. void RightBalance(BiTree *T) 85. { 86. BiTree R,Rl; 87.     R=(*T)->rchild; /* R指向T的右子樹根結點 */   
    88.     switch(R->bf) 89.     { /* 檢查T的右子樹的平衡度,並做相應平衡處理 */   
    90.      case RH: /* 新結點插入在T的右孩子的右子樹上,要做單左旋處理 */   
    91.               (*T)->bf=R->bf=EH; 92. L_Rotate(T); 93.               break; 94.      case LH: /* 新結點插入在T的右孩子的左子樹上,要做雙旋處理 */   
    95.               Rl=R->lchild; /* Rl指向T的右孩子的左子樹根 */   
    96.               switch(Rl->bf) 97.               { /* 修改T及其右孩子的平衡因子 */   
    98.                 case RH: (*T)->bf=LH; 99.                          R->bf=EH; 100.                          break; 101.                 case EH: (*T)->bf=R->bf=EH; 102.                          break; 103.                 case LH: (*T)->bf=EH; 104.                          R->bf=RH; 105.                          break; 106. } 107.               Rl->bf=EH; 108.               R_Rotate(&(*T)->rchild); /* 對T的右子樹做右旋平衡處理 */   
    109.               L_Rotate(T); /* 對T做左旋平衡處理 */   
    110. } 111. } 112. 113. /* 若在平衡的二叉排序樹T中不存在和e有相同關鍵字的結點,則插入一個 */   
    114. /* 數據元素爲e的新結點,並返回1,不然返回0。若因插入而使二叉排序樹 */   
    115. /* 失去平衡,則做平衡旋轉處理,布爾變量taller反映T長高與否。 */  
    116. Status InsertAVL(BiTree *T,int e,Status *taller) 117. { 118.     if(!*T) 119.     { /* 插入新結點,樹「長高」,置taller爲TRUE */   
    120.          *T=(BiTree)malloc(sizeof(BiTNode)); 121.          (*T)->data=e; (*T)->lchild=(*T)->rchild=NULL; (*T)->bf=EH; 122.          *taller=TRUE; 123. } 124.     else  
    125. { 126.         if (e==(*T)->data) 127.         { /* 樹中已存在和e有相同關鍵字的結點則再也不插入 */   
    128.             *taller=FALSE; return FALSE; 129. } 130.         if (e<(*T)->data) 131.         { /* 應繼續在T的左子樹中進行搜索 */   
    132.             if(!InsertAVL(&(*T)->lchild,e,taller)) /* 未插入 */   
    133.                 return FALSE; 134.             if(*taller) /* 已插入到T的左子樹中且左子樹「長高」 */   
    135.                 switch((*T)->bf) /* 檢查T的平衡度 */   
    136. { 137.                     case LH: /* 本來左子樹比右子樹高,須要做左平衡處理 */   
    138.                             LeftBalance(T); *taller=FALSE; break; 139.                     case EH: /* 本來左、右子樹等高,現因左子樹增高而使樹增高 */   
    140.                             (*T)->bf=LH; *taller=TRUE; break; 141.                     case RH: /* 本來右子樹比左子樹高,現左、右子樹等高 */    
    142.                             (*T)->bf=EH; *taller=FALSE; break; 143. } 144. } 145.         else  
    146.         { /* 應繼續在T的右子樹中進行搜索 */   
    147.             if(!InsertAVL(&(*T)->rchild,e,taller)) /* 未插入 */   
    148.                 return FALSE; 149.             if(*taller) /* 已插入到T的右子樹且右子樹「長高」 */   
    150.                 switch((*T)->bf) /* 檢查T的平衡度 */   
    151. { 152.                     case LH: /* 本來左子樹比右子樹高,現左、右子樹等高 */   
    153.                             (*T)->bf=EH; *taller=FALSE;  break; 154.                     case EH: /* 本來左、右子樹等高,現因右子樹增高而使樹增高 */  
    155.                             (*T)->bf=RH; *taller=TRUE; break; 156.                     case RH: /* 本來右子樹比左子樹高,須要做右平衡處理 */   
    157.                             RightBalance(T); *taller=FALSE; break; 158. } 159. } 160. } 161.     return TRUE; 162. } 163. 164. int main(void) 165. { 166.     int i; 167.     int a[10]={3,2,1,4,5,6,7,10,9,8}; 168.     BiTree T=NULL; 169. Status taller; 170.     for(i=0;i<10;i++) 171. { 172.         InsertAVL(&T,a[i],&taller); 173. } 174.     printf("本樣例建議斷點跟蹤查看平衡二叉樹結構"); 175.     return 0; 176. }  

 首先總體看一遍InsertAVL函數代碼,結合上面的圖,我想以前兩個問題都會有答案了,再重複下。

一、既然旋轉是經過平衡因子|bf|>1來決定怎麼旋轉的,那麼在旋轉前這些平衡因子是何時賦值呢?

在元素插入以後,首先認爲該元素就是一個子樹,從無到有,故高度增長taller=true,而後開始回溯到上一個節點根據元素插入後,對其的三種影響來調整平衡因子,同時從新賦值taller。

二、旋轉以後,哪些節點須要調整?平衡因子又改如何調整呢?

由於平衡二叉樹自己也是排序樹,就上圖以左子樹高爲列,若能直接右旋,影響的節點有T、L,最後會爲T.bf=L.bf=EH。

若要先左旋再右旋,則根據LR的取值再分三種狀況(最後會變成LR爲根,LR.bf=EH)

一、 LR.bf=EH:旋轉後 T.bf=L.bf=EH

二、 LR.bf=LH:旋轉後 T.bf=RH,L.bf=EH

三、 LR.bf=RH:旋轉後 T.bf=EH,L.bf=LH

 

右子樹高的狀況同理。

要是你對java實現不興趣,能夠不用往下看了,數據結構主要是其思想,實現只須要根據語言特性來稍做改變。

java代碼:

思路跟上面代碼同樣(原諒我偷懶不寫註釋...),主要是多了個rootAVL和preNode。緣由是c語言函數傳值能夠直接傳指針,這樣對於參數的修改會反應到被調函數外面。而java都是值傳遞,方法內操做的只是引用的一個副本,他們指向的地址相同而已,只能修改引用所指向的內存,修改引用副本是不會影響引用自己的。 

故須要單獨處理節點的前驅和根節點,  rootAVL用來保存最後的根節點,由於每次插入都須要從根節點開始遞歸。

preNode表示每次回溯時的前面一個節點。

 

public class AVL {

    private boolean taller=false;
    private Node root =null;
    private static final int EH=0;
    private static final int LH=1;
    private static final int RH=-1;

    private class Node{
        public int data;
        public Node leftChild;
        public Node rightChild;
        public int balanceFactor;

        public Node(int data){
            this.data=data;
            balanceFactor=0;
        }
    }

    public  Node RRotate(Node T){
        Node temp=T.leftChild;
        T.leftChild=temp.rightChild;
        temp.rightChild=T;
        return temp;
    }

    public  Node LRotate(Node T){
        Node temp=T.rightChild;
        T.rightChild=temp.leftChild;
        temp.leftChild=T;
        return  temp;
    }

    public  Node leftBalance(Node node,Node preNode){
        Node child=node.leftChild;
        Node root=null;
        switch (child.balanceFactor){
            case LH:
                node.balanceFactor=child.balanceFactor=EH;
                root=RRotate(node);
                if(preNode!=null && node.data< preNode.data){
                    preNode.leftChild=root;
                }
                if(preNode!=null && node.data> preNode.data){
                    preNode.rightChild=root;
                }
                break;
            case RH:
                Node rchild=child.rightChild;
                switch (rchild.balanceFactor){
                    case EH:
                        node.balanceFactor=child.balanceFactor=EH;
                        break;
                    case LH:
                        node.balanceFactor=RH;
                        child.balanceFactor=EH;
                        break;
                    case RH:
                        node.balanceFactor=EH;
                        child.balanceFactor=LH;
                        break;
                    default:break;
                }
                rchild.balanceFactor=EH;
                node.leftChild=LRotate(child);
                root=RRotate(node);
                if(preNode!=null && node.data<preNode.data){
                    preNode.leftChild=root;
                }
                if(preNode!=null && node.data>preNode.data){
                    preNode.rightChild=root;
                }
                break;
            default:break;
        }
        return root;
    }

    public  Node rightBalance(Node node,Node preNode){
        Node child=node.rightChild;
        Node root=null;
        switch (child.balanceFactor){
            case RH:
                node.balanceFactor=child.balanceFactor=EH;
                root=LRotate(node);
                if(preNode!=null && node.data<preNode.data){
                    preNode.leftChild=root;
                }
                if(preNode!=null && node.data>preNode.data){
                    preNode.rightChild=root;
                }
                break;
            case LH:
                Node lchild=child.leftChild;
                switch (lchild.balanceFactor){
                    case EH:
                        node.balanceFactor=child.balanceFactor=EH;
                        break;
                    case RH:
                        node.balanceFactor=LH;
                        child.balanceFactor=EH;
                        break;
                    case LH:
                        node.balanceFactor=EH;
                        child.balanceFactor=RH;
                        break;
                    default:break;
                }
                lchild.balanceFactor=EH;
                node.rightChild=RRotate(child);
                root=LRotate(node);
                if(preNode!=null && node.data<preNode.data){
                    preNode.leftChild=root;
                }
                if( preNode!=null && node.data>preNode.data){
                    preNode.rightChild=root;
                }
                break;
            default:break;
        }
        return root;
    }

    public boolean insertNode(int value){
       return insertNode(root,value,null);
    }

    public  boolean insertNode(Node node, int value, Node preNode){
        if(node==null){
            node=new Node(value);
            node.balanceFactor=EH;
            taller=true;
            if(preNode!=null && node.data< preNode.data){
                preNode.leftChild=node;
            }
            if(preNode!=null && node.data> preNode.data){
                preNode.rightChild=node;
            }
            root =node;
            return true;
        }else{
            if(value==node.data){
                root =node;
                return false;
            }
            if (value<node.data){
                if (!insertNode(node.leftChild, value, node)) {
                    root =node;
                    return false;
                }
                if(taller){
                    switch (node.balanceFactor){
                        case EH:taller=true;node.balanceFactor=LH;break;
                        case RH:taller=false;node.balanceFactor=EH;break;
                        case LH:
                            taller=false;
                            node=leftBalance(node,preNode);
                            if(preNode!=null){
                                node=preNode;
                            }
                            break;
                        default:break;
                    }
                }
            }
            if (value>node.data){
                if (!insertNode(node.rightChild, value,node)) {
                    root =node;
                    return false;
                }
                if(taller){
                    switch (node.balanceFactor){
                        case EH:taller=true;node.balanceFactor=RH;break;
                        case LH:taller=false;node.balanceFactor=EH;break;
                        case RH:
                            taller=false;
                            node=rightBalance(node,preNode);
                            if(preNode!=null){
                                node=preNode;
                            }
                            break;
                        default:break;
                    }
                }
            }
        }
        root =node;
        return true;
    }

    public void inorderTraversal(){
        inorderTraversal(root);
    }

    public  void inorderTraversal(Node root){
        if(root!=null){
            inorderTraversal(root.leftChild);
            System.out.println("節點:"+root.data+"  平衡因子:"+root.balanceFactor);
            inorderTraversal(root.rightChild);
        }
        return ;
    }

    public static void main(String[] args) {
        //int[] data={8,6,4};
        //int[] data={8,6,9,5,7,3};
        //int[] data={8,6,7};
        //int[] data={8,5,9,4,6,7};
        //int[] data={8,5,9,4,7,6};
        int[] data={8,5,9,7,6};
        AVL avl=new AVL();
        for(int i=0;i<data.length;i++){
            avl.insertNode(data[i]);
        }
        avl.inorderTraversal();
    }
}
相關文章
相關標籤/搜索