1. 程式人生 > >看圖說話之平衡二叉排序樹(AVL樹)

看圖說話之平衡二叉排序樹(AVL樹)

看圖說話之平衡二叉排序樹

本文在看圖說話之二叉排序樹的基礎上介紹了平衡二叉排序樹,結構性較好的二叉排序樹其插入和刪除操作的時間複雜度接近Log(N),但是存在某些特定的情況二叉排序樹會退化到單鏈表的情況,比如由順序或者逆序或者基本有序的陣列構建的排序二叉樹,此時插入和刪除操就上升到了Log(N)的時間複雜度。下圖所示就是結構性良好和退化的二叉排序樹。


               圖1 最優BST樹形結構和最差BST樹形

      為了解決二叉排序樹的這種退化的情況,在其基礎上提出了平衡二叉排序樹,實現平衡二叉排序樹的方法有很多種,其中最基礎就是AVL樹,本文詳細的介紹下AVL樹實現平衡的基本原理以保證二叉排排序樹具有Log(N)的時間複雜界。

      一丶平衡二叉排序樹

      平衡二叉排序樹如其名字在二叉樹上約定了平衡條件,通過下圖這個平衡二叉樹和非平衡二叉樹來說明平衡二叉樹的平衡條件。

    

                   圖2 平衡和非平衡二叉樹

      平衡性,是指每個節點的左子樹高度和右子樹高度之差不超過1,即 Math.abs(Hieght(node.left) –Height(node.right))<1。對於圖2中的平衡二叉樹而言,其上每個節點都滿足這個性質。圖2中的非平衡樹,之所以不是平衡樹,是因為在根節點處平衡性遭到了破壞,其左子樹高度和右子樹高度之差為2。

      二丶AVL樹平衡性調整策略

      我覺得研究AVL樹調整平衡性策略的正確姿勢應該包括兩步,第一步研究導致二叉排序樹不平衡的情況是哪幾種。第二步針對具體的不平衡情況如何調整。

      先研究二叉排序樹的不平衡情況,然後針對二叉排序樹的不平衡特點給出解決方案:

      (1) 向節點的左兒子的左子樹插入元素導致的平衡性破壞。


                       圖3 第一種非平衡情況

      可以看到,插入元素1後,是節點10處的平衡性遭到了破壞。這裡要強調一下,在更一般的情況下,插入元素有破壞平衡性的可能,而平衡性被破壞的節點可能發生在插入路徑上的每一個節點,可能是父節點,可能是其他節點

      對於該種平衡性破壞給出的解決方法稱為左旋轉其過程如下圖所示:


                      圖 4Single左旋轉平衡性調整

通過圖4可以清晰的看出Single左旋轉調整平衡性的特點,利用文字描述過於拗口,利用虛擬碼描述如下:假設平衡性被破壞的節點是node。

//調整node節點平衡性
node = SingleLeftRotation(node);
//balacne函式定義如下
public node SingleLeftRotation( (Nodenode){
        if(node ==null) return null;
           Node newRoot= node.left;
           node.left = newRoot.right;
           newRoot.right = node;
           return newRoot;
}
(2)向節點的右兒子的右子樹插入元素導致的平衡性破壞。


 圖5第二種非平衡情況

該種情況下的非平衡性情況十分清晰,並且和第一種非平衡性情況是對稱的,不妨成這種非平衡性破壞為“右右“,第一種非平衡性破壞為”左左“。該種情況下的調整過程如圖所示,這調整過程稱為single右旋轉。

      

           圖6 single右旋轉調整

上述調整過程用虛擬碼實現如下:

//調整node節點平衡性
node = SingleRightRotation(node);
//balacne函式定義如下
public node SingleRightRotation( (Nodenode){
        if(node==null) return null;
           Node newRoot= node.right;
           node.right = newRoot.left;
           newRoot.left= node;
           return newRoot;
}
(3)向節點的左兒子的右子樹插入元素導致平衡性破壞

            圖7 第三種平衡破壞情況

這種不平衡性的情況,形象的將其稱之為“左右“不平衡,該情況的不平衡的過程如下圖所示:

                      圖 8 double左旋轉調整

      圖8清晰的說明了調整平衡性的過程,在掌握的single左旋轉和single右旋轉的基礎上看懂上述過程絲毫不苦難,上述過程稱為double左旋轉利用虛擬碼描述如下:

//調整node節點平衡性
node = dounleLeftRotation (node);
//balacne函式定義如下
public node dounleLeftRotation(Nodenode){
           node.left=singleRightRotation(node.left);
           node = singleLeftRotation(node);
           return node;
}
(4)向節點的右兒子的左子樹插入元素導致平衡性破壞

                      圖9 第四種不平衡情況

 該種平衡情況是向右兒子的左子樹插入元素導致不平衡,這種情況形象的稱之為“右左“不平衡,和”左右“不平衡是對稱的處理的手段都是類似的。下圖詳細的描述了這種情況如何調整平衡

                   

 圖10 double右旋轉

通過圖10可以清晰的看到調整平衡的過程,上述過程和double左旋轉是對稱的操作,利用虛擬碼描述如下:

  //調整node節點平衡性
node = dounleRightRotation (node);
//balacne函式定義如下
public node dounleLeftRotation(Nodenode){
           node.left=singleLeftRotation(node.left);
           node = singleRightRotation(node);
           return node;
}
三丶AVL樹完整的java實現
public classAVLTree {
    publicNode root = null;
     publicstatic void main(String []args){
       int[] elements = new int[]{1,2,3,4,5};
       AVLTree avl = new AVLTree();
       /****測試插入,建樹和中序遍歷操作***/
       avl.buildTree(elements);
       /********測試二叉樹的高度***************/
       System.out.println(avl.root.height);
    //  bst.midTrace();
    //  System.out.println();
       /****測數刪除操作之無兒子***************/
    //  bst.delete(9);
    //  bst.midTrace();
       /****測數刪除操作之只有一個兒子***************/
    //  bst.delete(4);
    //  bst.midTrace();
       /****測數刪除操作只有兩個兒子***************/
    //  bst.delete(5);
    //  bst.midTrace();
    }
   
    //節點型別定義
    publicstatic class Node{
       publicint element;
       publicNode left;
       publicNode right;
       //平衡二叉排序樹,為了保證平衡,對每個節點維護了高度資訊
       publicint  height;
       publicNode(int element){
           this.element = element;
           left = null;
           right = null;
           height = 0;
       }
    }
    //外部使用的插入方法
    publicvoid insert(int element){
       root=insert(element,root);
    }
    //內部使用的插入方法
    private  Node insert(int element,Node root){
       if(root==null)
           return new Node(element);
       intdelta = element-root.element;
       if(delta<0){
           root.left = insert(element,root.left);
       }elseif(delta>0){
           root.right = insert(element,root.right);
       }else{
           //不做處理
       }
       returnbalanced(root);
    }
    //外部使用的建樹方法
    publicvoid buildTree(int [] elements){
       if(root==null){
           for(int i=0;i<elements.length;i++){
              insert(elements[i]);
           }
       }else{
           //若樹以存在,則不能建
       }
    }
    publicvoid delete(int element){
       delete(element,root);
    }
    publicNode delete(int element,Node root){
       //未定位到刪除元素
       if(root==null) return null;
       intdelta = element-root.element;
       if(delta<0){
           root.left = delete(element,root.left);
       }elseif(delta>0){
           root.right =delete(element,root.right);
       }else{
           if(root.left!=null&&root.right!=null){
              Node node = findMin(root.right);
              root.element =node.element;
              root.right = delete(node.element,root.right);
           }else{
              root = root.left!=null?root.left:root.right;
           }
       }
       returnbalanced(root);
    }
    //中序遍歷二叉樹
    publicvoid midTrace(){
       if(root!=null){
           print(root);
       }
    }
    privatevoid print(Node node){
       if(node!=null){
           print(node.left);
           System.out.print(node.element+" ");
           print(node.right);
       }
    }
    //找到該節點下子樹的最小節點。
    publicNode findMin(Node node){
       if(node.left==null)
           return node;
       else
          
           return findMin(node.left);
    }
    publicNode balanced(Node node){
       node.height= Math.max(getHeight(node.left),getHeight(node.right))+1;
       if(getHeight(node.left)-getHeight(node.right)>1){
           if(getHeight(node.left.left)>getHeight(node.left.right)){
              node = singleLeftRotation(node);
           }else{
              node = doubleLeftRotation(node);
           }
       }
       if(getHeight(node.right)-getHeight(node.left)>1){
           if(getHeight(node.right.left)>getHeight(node.right.right)){
              node =doubleLeftRotation(node);
           }else{
              node = singleRightRotation(node);
           }
       }
       returnnode;
    }
    publicint getHeight(Node node){
       returnnode==null?-1:node.height;
    }
    publicNode singleLeftRotation(Node node){
       Node newRoot = node.left;
       node.left = newRoot.right;
       //左右子樹變化更新高度
       node.height = Math.max(getHeight(node.left),getHeight(node.right))+1;
       newRoot.right=node;
       //左右子樹變化更新高度
       newRoot.height=  Math.max(getHeight(newRoot.left),getHeight(newRoot.right))+1;
       returnnewRoot;
    }
    publicNode singleRightRotation(Node node){
       Node newRoot = node.right;
       node.right = newRoot.left;
       //左右子樹變化更新高度
       node.height = Math.max(getHeight(node.left),getHeight(node.right))+1;
       newRoot.left=node;
       //左右子樹變化更新高度
       newRoot.height=  Math.max(getHeight(newRoot.left),getHeight(newRoot.right))+1;
       returnnewRoot;
    }
    publicNode doubelRightRotation(Node node){
       node.right = singleLeftRotation(node.right);
       returnsingleRightRotation(node);
    }
    publicNode doubleLeftRotation(Node node){
       node.left = singleRightRotation(node.left);
       returnsingleLeftRotation(node);
    }
}