平衡二叉樹(AVL tree)


二叉查找樹在極端情況下會演變成一棵只有一側子孩子的樹,例如每個非葉子只有左孩子或者右孩子,這時候在查找的時候就需要遍歷這棵樹來找到目標值,它的快速搜索價值就體現不出來了,如果這棵搜索樹在構建的時候,能夠平衡左右子樹的身高差,使得左右子樹身高差不超過1,那它的搜索效率就是O(lgn),平衡二叉樹就是這樣的樹,它有以下特點:

1、左右子樹的高度差不超過1

2、左右子樹都是平衡二叉樹,空節點也視其為一棵平衡二叉樹

以上有點遞歸定義問題的意思,平衡二叉樹的關鍵特性是其任何一個節點為根的左右子樹高度差不能超過1,因為有這個特性限制,所以在創建平衡二叉樹的時候如果發現這個平衡被打破了,需要對樹進行旋轉操作,以恢復其平衡的性質,具體的旋轉有以下幾種情況:

1、LL類型旋轉(單向向右):這種情況下是某個節點的左孩子的左子樹導致該節點失衡,需要對該節點進行向右旋轉,如下圖:

           

 

 

 如上圖,值為3的節點,當它只有一個左孩子2時,此時左右孩子的高度差是1,當再插入1時,它的左右孩子的高度差變成了2,需要對其進行右旋:

LL_Rotate(node):

  leftNode=node.left;//待旋轉節點的左孩子

  leftRightNode=leftNode;//待旋轉節點的左孩子的右孩子

  leftNode.right=node;//更新左孩子節點,將自己作為作為左孩子的右節點,即上圖中3變成了2的右孩子

  node.left=leftRightNode;//更新自己的左孩子,

  node.height=max(node.left.height,node.right.height)+1;//更新高度

  leftNode.height=max(leftNode.left.height,leftNode.right.height)+1;//跟新新的根節點高度,需要先更新node節點...

  return leftNode;//返回左孩子替代自己的位置,比如上圖中,2替代了3的位置

 

2、RR類型旋轉(單向向左):這種情況下是某個節點的右孩子的右子樹導致該節點失衡,需要對該節點進行向左旋轉,如下圖:

     

 如上圖,因為3的插入,根節點1的平衡因子變成2,需要對1進行左旋,方向與LL完全相反:

RR_Rotate(node):

  rightNode=node.right;//獲取右孩子

  rightLeftNode=rightNode.left;//獲取右孩子的左孩子

  rightNode.left=node;//更新右孩子

  node.right=rightLeftNode;

  node.height=max(node.left.height,node.right.height)+1;

  rightNode.height=max(rightNode.left.height,right.right.height)+1;

  return rightNode;//返回

3、LR類型旋轉(先左后右):這種情況下是某個節點的左孩子的右子樹導致該節點失衡,需要對該節點的左孩子進行左旋,然后再對其進行右旋,如下圖:

     

如上圖,節點值為8的節點的左孩子,因為6的插入,導致8節點失衡,需要先對左孩子4進行左旋,由6替代4的位置,再對8進行右旋,由6替代8:

Left_Right_Rotate(node):

  node.left=LL_Rotate(node.left);//對左孩子左旋

  return RR_Rotate(node);//對自己進行右旋

4、RL類型旋轉(先右后左):這種情況下是某個節點的右孩子的左子樹導致該節點失衡,需要對該節點的右孩子進行右旋,然后再對其進行左旋,如下圖:

       

如上圖,4的平衡因子因為其右孩子7新增了一個左孩子節點6而打破,需要對節點7先右旋,再對4進行左旋,同LR完全相反,偽代碼如下:

Right_Left_Rotate(node):

  node.right=RR_Rotate(node.right);//對右孩子進行右旋

  return LL_Rotate(node);//對自身進行左旋

節點的高度的獲取:

Get_Height(node):

  if node==null:

    return 0;

  leftHeight=Get_Height(node.left);

  rightHeight=Get_Height(node.right);

  return max(leftHeight,rightHeight)+1;

平衡因子的獲取,某個節點的平衡因子是由左右子樹的高度差決定的:

Get_Factor(node):

  return Get_Height(node.left)-Get_Height(node.right);

節點的平衡調整,涉及到左旋、右旋、左右旋轉、右左旋轉:

balance(node):

  if node==null 

    return null;

  //左子樹導致不平衡

  if getFactor(node)==2 :

    //左孩子的左子樹導致不平衡

    if getFactor(node.left)==1:

      node=LL_Rotate(node)

    else:

      //左孩子的右子樹導致不平衡

      node=Left_Right_Rotate(node);

  //右子樹導致不平衡

  else if getFactor(node)==-2:

    右子樹的右孩子導致不平衡

    if getFactor(node.right)==-1:

      node=RR_Rotate(node);

    else

      //右孩子的左子樹導致不平衡

      node=Right_Left_Rotate(node);

  //返回旋轉后的節點

  return node;

 

樹的插入操作:

insert_val(node,val):

  if node==null:

    return new Node(val);

  //這里是一個遞歸創建過程

  if val<node.val:

    node.left=insert_val(node.left,val);

    node.left.parent=node;

  else

    node.right=insert_val(node.right,val);

    node.right.parent=node;

  //返回調整后的節點

  return balance(node);

樹的刪除操作,刪除操作有點復雜,需要找到那個替代自己的節點,其方法就是尋找刪除節點的左子樹的最大值;如果待刪除節點沒有左子樹,則需要尋找

右子樹的最小值,如果待刪除的節點是葉子節點,則直接刪除即可:

delete_node(node,val):

  if node==null:

    return;//沒有找到,直接返回

  if val < node.val:

    delete_node(node.left,val);

  else if val > node.val:

    delete_node(node.right,val);

  else: //尋找到了目標節點

    //目標節點是葉子節點

    if node.left==null && node.right==null:

      parent=node.parent;

      //待刪除的節點是根節點

      if parent==null:

        root=null;

      else if parent.left==node:

        parent.left=null;  

      else: //刪除節點

        parent.right=null;

    else if node.left!=null:

      left=node.left

      maxNode=left

      while left!=null:

        maxNode=right;

        left=left.right;

      node.val=maxNode.val

      delete_node(node.left,node.val);

    else: //與上面node.left!=null中的left相反,將left修改為right

  balance(node);//調節樹的平衡

以上AVL樹的構建分析完畢,其中關鍵點為節點的平衡操作,創建和刪除節點時使用到了遞歸操作,算是一個設計技巧,如下為完整的示例代碼:

 

package avl;

import java.util.ArrayList;
import java.util.List;

/**
 * AVL 樹的定義
 */
public class AVLTree {
    //根節點
    Node root=null;

    /**
     * 插入新值
     * @param val
     * @return
     */
    public AVLTree insertVal(int val){
        if(root==null){
            root=new Node(val);
        }else{
            insertVal(root,val);
        }
        return this;
    }

    /**
     * 節點的插入
     * @param node
     * @param val
     * @return
     */
    private Node insertVal(Node node,int val){
        if(node==null){
            node=new Node(val);
            return node;
        }
        if(val<node.val){
            Node left=insertVal(node.left,val);
            node.left=left;
            left.parent=node;
        }else{
            Node right=insertVal(node.right,val);
            node.right=right;
            right.parent=node;
        }
        //調整節點
        node=balance(node);
        return node;
    }

    /**
     * 刪除節點
     * @param val
     */
    public void deleteVal(int val){
        deleteVal(root,val);
    }

    private void deleteVal(Node node,int val){
        //沒有找到,直接返回
        if(node==null){
            return;
        }else if(val<node.val){
            deleteVal(node.left,val);
            balance(node);
        }else if(val>node.val){
            deleteVal(node.right,val);
            balance(node);
        }else{
            //葉子節點,直接刪除
            if(node.left==null && node.right==null){
                Node parent=node.parent;
                if(parent==null){
                    root=null;
                }
                if(parent.left==node){
                    parent.left=null;
                }else{
                    parent.right=null;
                }
            }else{
                //如果左子樹不為空,尋找其最大的后繼節點
                if(node.left!=null){
                    Node left=node.left;
                    Node maxNode=left;
                    //注意這里是怎么找最大的后繼節點的
                    while(left!=null){
                        maxNode=left;
                        left=left.right;
                    }
                    node.val=maxNode.val;
                    deleteVal(node.left,maxNode.val);
                    balance(node);
                }else{
                    Node right=node.right;
                    Node maxNode=right;
                    while(right!=null){
                        maxNode=right;
                        right=right.left;
                    }
                    node.val=maxNode.val;
                    deleteVal(node.right,maxNode.val);
                    balance(node);
                }
            }
        }

    }

    /**
     * 平衡節點的操作動作
     * @param node
     * @return
     */
    private Node balance(Node node){
        if(node==null){
            return null;
        }
        if(getFactor(node)==2){
            if(getFactor(node.left)==1){
                node= LL_Rotate(node);
            }else{
                node= LR_Rotate(node);
            }
        }else if(getFactor(node)==-2){
            if(getFactor(node.right)==-1){
                node= RR_Rotate(node);
            }else{
                node= RL_Rotate(node);
            }
        }
        return node;
    }

    /**
     * 獲取節點的高度
     * @param node
     * @return
     */
    private int getHeight(Node node){
        if(node==null){
            return 0;
        }
        int left=getHeight(node.left);
        int right=getHeight(node.right);
        int max=Math.max(left,right);
        return max+1;
    }

    /**
     * 獲取節點的平衡因子
     * @param node
     * @return
     */
    private int getFactor(Node node){
        if(node==null){
            return 0;
        }
        return getHeight(node.left)-getHeight(node.right);
    }

    /**
     * 先右后左
     * @param node
     * @return
     */
    private Node RL_Rotate(Node node){
        Node right=LL_Rotate(node.right);
        node.right=right;
        right.parent=node;
        return RR_Rotate(node);
    }

    /**
     * 先左后右
     * @param node
     * @return
     */
    private Node LR_Rotate(Node node){
        Node left=RR_Rotate(node.left);
        node.left=left;
        left.parent=node;
        return LL_Rotate(node);
    }

    /**
     * 單向左旋
     * @param node
     * @return
     */
    private Node RR_Rotate(Node node){
        Node right=node.right,parent=node.parent;
        Node rightLeft=right.left;
        right.left=node;
        node.parent=right;
        node.right=rightLeft;
        if(rightLeft!=null){
            rightLeft.parent=node;
        }
        right.parent=parent;
        if(parent!=null){
            if(parent.left==node){
                parent.left=right;
            }else{
                parent.right=right;
            }
        }else{
            root=right;
        }
        return right;
    }

    /**
     * 單向右旋
     * @param node
     * @return
     */
    private Node LL_Rotate(Node node){
        Node left=node.left,parent=node.parent;
        Node leftRight=left.right;
        left.right=node;
        node.parent=left;
        node.left=leftRight;
        if(leftRight!=null){
            leftRight.parent=node;
        }
        left.parent=parent;
        if(parent!=null){
            if(parent.left==node){
                parent.left=left;
            }else{
                parent.right=left;
            }
        }else{
            root=left;
        }
        return left;
    }

    /**
     * 先序遍歷
     * @param node
     */
    public void preOrder(Node node){
        if(node!=null){
            System.out.print(node);
            preOrder(node.left);
            preOrder(node.right);
        }
    }

    /**
     * 中序遍歷
     * @param node
     */
    public void inOrder(Node node){
        if(node!=null){
            inOrder(node.left);
            System.out.print(node);
            inOrder(node.right);
        }
    }

    /**
     * 后序遍歷
     * @param node
     */
    public void postOrder(Node node){
        if(node!=null){
            postOrder(node.left);
            postOrder(node.right);
            System.out.print(node);
        }
    }

    /**
     * 按層遍歷樹
     */
    public void printByLevel(){
        System.out.println("=========================");
        List<Node> temp=new ArrayList<>();
        if(root!=null){
            temp.add(root);
        }
        while(temp.size()>0){
            List<Node> nodes=new ArrayList<>();
            temp.stream().forEach(obj-> {
                System.out.print(obj);
                if(obj.left!=null){
                    nodes.add(obj.left);
                }
                if(obj.right!=null){
                    nodes.add(obj.right);
                }
            });
            System.out.println();
            temp.clear();
            temp.addAll(nodes);
        }
    }

    public static void main(String[] args) {
        AVLTree tree=new AVLTree();
        tree.insertVal(1).insertVal(2).insertVal(3).insertVal(4).insertVal(5).insertVal(7).insertVal(6);
        tree.printByLevel();
        tree.deleteVal(6);
        tree.printByLevel();
        tree.deleteVal(4);
        tree.printByLevel();
    }
}

class Node{
    public int val;
    public Node left,right,parent;
    public Node(int val){
        this.val=val;
    }

    @Override
    public String toString() {
        return val+" ";
    }
}
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM