24. 平衡二叉樹,及其程式碼實現

乖乖虎學Java發表於2020-10-21

1.什麼是平衡二叉樹

  • 平衡樹(Balance Tree,BT) 指的是,任意節點的子樹的高度差都小於等於1
  • 他是二叉排序樹的改進,解決了二叉排序樹的一些小問題
  • 平衡二叉樹一定是二叉排序樹

在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述
右子樹的高度比左子樹的高度要高的時候進行左旋轉

左旋轉的目的是為了降低右子樹的高度

在這裡插入圖片描述

在這裡插入圖片描述
左子樹的高度比右子樹的高度要高的時候進行右旋轉

右旋轉的目的是為了降低左子樹的高度

在這裡插入圖片描述

在這裡插入圖片描述

這個明顯通過單個方向的旋轉解決不了問題,需要使用雙旋轉
在這裡插入圖片描述

在這裡插入圖片描述

  • 什麼情況下需要使用雙旋轉:
  • 他的左子樹的右子樹高度大於他的左子樹高度
  • 解決方案:
  • 先對當前節點的左節點進行左旋轉
  • 在對當前節點進行右旋轉操作

2.程式碼實現

package com.qin.avl;

//平衡二叉樹
public class AVLTreeDemo {

    public static void main(String[] args) {

        int[] arr = {10,11,7,6,8,9};

        //建立一個AVLTree物件
        AVLTree avlTree = new AVLTree();

        //新增節點
        for (int i = 0; i < arr.length; i++) {
            avlTree.add(new Node(arr[i]));
        }



        //中序遍歷
        System.out.println("中序遍歷");
        avlTree.infixOrder();


        System.out.println("==================");
        System.out.println("在旋轉之後");
        System.out.println("樹的高度"+avlTree.getRoot().height());
        System.out.println("樹的左子樹高度"+avlTree.getRoot().leftHeight());
        System.out.println("樹的右子樹高度"+avlTree.getRoot().rightHeight());


    }



}

//建立AVL Tree
class AVLTree{

    private Node root;

    public Node getRoot() {
        return root;
    }

    //新增節點的方法
    public void add(Node node){
        if (root==null){
            root = node; //如果root為空,則直接讓root指向node
        }else {
            root.add(node);
        }
    }

    //中序遍歷
    public void infixOrder(){
        if (root!=null){
            root.infixOrder();
        }else {
            System.out.println("當前二叉排序樹是空的,無法遍歷");
        }
    }

}

//建立node節點
class Node{

    int value;
    Node left;
    Node right;

    public Node(int value) {
        this.value = value;
    }

    @Override
    public String toString() {
        return "Node{" +
                "value=" + value +
                '}';
    }

    //新增節點的方法
    //遞迴的形式新增節點,注意需要滿足二叉排序樹的要求
    public void add(Node node){
        if (node == null){
            return;
        }
        //判斷傳入節點的值和當前子樹根節點的值得關係
        if (node.value<this.value){ //輸入節點的值小於當前節點的值
            if (this.left==null){ //如果當前節點的左子節點為空,那麼就掛載當前節點的左子節點
                this.left = node;
            }else {
                //遞迴的向左子樹新增
                this.left.add(node);
            }
        }else {
            if (this.right==null){
                this.right = node;
            }else {
                //遞迴向右子樹新增
                this.right.add(node);
            }
        }

        //當新增完一個節點後,右子樹的高度-左子樹的高度的絕對值大於1 ,左旋轉
        if (rightHeight()-leftHeight()>1){
            //如果他的右子樹的左子樹的高度大於它的右子樹的高度
            if (right!=null && right.left.height()>right.height()){
                //對當前節點的右節點進行右旋轉
                right.rightRotate();
                //在對當前節點進行左旋轉
                leftRotate();
            }else {
                //否者對當前節點直接左旋轉即可
                leftRotate(); //左旋轉
            }
            return; //必須要!!!

        }
        if (leftHeight()-rightHeight()>1){ //右旋轉
            //如果他的左子樹的右子樹的高度大於他的左子樹的高度
            if (left!=null && left.right.height()>left.leftHeight()){
                //先對當前節點的左節點(左子樹)進行左旋轉
                left.leftRotate();
                //在對當前節點進行右旋轉
                rightRotate();
            }else {
                //不滿足條件,直接右旋轉即可
                rightRotate();
            }

        }



    }

    //中序遍歷
    public void infixOrder(){
        if (this.left!=null){
            this.left.infixOrder();
        }
        System.out.println(this);
        if (this.right!=null){
            this.right.infixOrder();
        }
    }
    
    //返回以該節點為根節點,這顆樹的高度
    public int height(){
        return Math.max(left==null?0:left.height(),right==null?0:right.height())+1;
    }

    //返回左子樹的高度
    public int leftHeight(){
        if (left==null){
            return 0;
        }
        return left.height();
    }

    //返回右子樹的高度
    public int rightHeight(){
        if (right==null){
            return 0;
        }
        return right.height();
    }

    //左旋轉的方法
    private void leftRotate(){
        //建立新的節點,以當前根節點的值
        Node newNode = new Node(value);
        //把新的節點的左子樹設定為當前節點的左子樹
        newNode.left = left;
        //把新的節點的右子樹設定成當前節點的右子樹的左子樹
        newNode.right = right.left;
        //把當前節點的值替換成右子節點的值
        value = right.value;
        //把當前節點的右子樹設定成當前節點右子樹的右子樹
        right = right.right;
        //把當前節點的左子節點設定成新的節點
        left = newNode;
    }

    //右旋轉的方法
    private void rightRotate(){
        //建立新的節點,以當前根節點的值
        Node newNode = new Node(value);
        //把新的節點的右子樹設定為當前節點的右子樹
        newNode.right = right;
        //把新的節點的左子樹設定成當前節點的左子樹的右子樹
        newNode.left = left.right;
        //把當前節點的值替換為左子節點的值
        value = left.value;
        //把當前節點的左子樹設定成當前節點左子樹的左子樹
        left = left.left;
        right = newNode;
    }





}

在這裡插入圖片描述

相關文章