avl樹的java實現(平衡二叉樹)

平衡二叉樹定義(AVL):
(1)它的左子樹和右子樹的深度之差(平衡因子)的絕對值不超過1。
(2)它的左子樹和右子樹都是一顆平衡二叉樹。

平衡因子:
將二叉樹上節點的左子樹深度減去右子樹深度的值稱爲平衡因子BF。則平衡二叉樹上所有節點的平衡因子只可能是1,-1,0。只要二叉樹上有一個節點的平衡因子的絕對值大於1,那麼該二叉樹就是不平衡的。
最小不平衡子樹:
距離插入節點最近的,且平衡因子的絕對值大於1的節點爲根的子樹,我們稱之爲最小不平衡子樹。

我們之間通過代碼來看實現:
1、二叉樹的定義

  class AVLNode {
        /**
         * 保存節點的數據
         */
        private int data;
        /**
         * 當前節點的深度
         * 深度是常用參數,保存深度數據,
         * 不用多次遞歸去計算節點的深度
         */
        private int depth;

        /**
         * 平衡指數(左邊深度-右邊深度)
         * 左子樹高則爲正,左邊子樹第則爲負數
         */
        private int balance;
        /**
         * 指向父節點(簡化後面關於循環父節點獲取)
         */
        public AVLNode parent;

        /**
         * 左子樹
         */
        public AVLNode left;

        /**
         * 右子樹
         */
        public AVLNode right;

        public AVLNode(int data) {
            this.data = data;
            depth = 1;
            balance = 0;
            left = null;
            right = null;
        }
    }

2、關於樹的深度的計算(因爲這裏的二叉樹定義了高度,直接用左右子樹的最高深度就可以了)

   /**
     * 計算當前節點深度
     *
     * @param node
     * @return
     */
    private int calculateDepth(AVLNode node) {
        int depth = 0;
        if (node.left != null) {
            depth = node.left.depth;
        }
        if (node.right != null && node.right.depth > depth) {
            depth = node.right.depth;
        }
        depth++;
        return depth;
    }

3、計算平衡因子()

 /**
     * 計算平衡因子
     *
     * @param node
     * @return
     */
    private int calculateBalance(AVLNode node) {
        int leftDepth;
        int rightDepth;
        //獲取節點左邊深度
        if (node.left != null) {
            leftDepth = node.left.depth;
        } else {
            leftDepth = 0;
        }

        if (node.right != null) {
            rightDepth = node.right.depth;
        } else {
            rightDepth = 0;
        }
        return leftDepth - rightDepth;
    }

4、右旋
在這裏插入圖片描述

   /**
     * 右旋
     *
     * @param node
     */
    private void rightRotate(AVLNode node) {
        AVLNode nParent = node.parent;
        AVLNode nLeftSon = node.left;
        AVLNode nLeftRightGrandSon = nLeftSon.right;


        if (nParent == null) {
            //更改更改根節點
            this.root = nLeftSon;
        }
        //1、更改父節點指向
        //在操作的時候只改變指針指向,故可以用==
        else if (node == nParent.left) {
            nParent.left = nLeftSon;
        } else if (node == nParent.right) {
            nParent.right = nLeftSon;
        }


        //2、更改左子節點指針指向(父節點和右子節點改變)
        nLeftSon.parent = nParent;
        nLeftSon.right = node;

        //3、更改當前節點
        node.parent = nLeftSon;
        node.left = nLeftRightGrandSon;

        //4、更改右子節點
        if (nLeftRightGrandSon != null) {
            nLeftRightGrandSon.parent = node;
        }

        //此時改變平衡度/深度其實就左子節點和當前節點
        //需要先計算當前節點的深度和平衡度,
        // 因爲原來的左子節點變成當前節點的父節點
        node.depth = calculateDepth(node);
        node.balance = calculateBalance(node);

        nLeftSon.depth = calculateDepth(nLeftSon);
        nLeftSon.balance = calculateBalance(nLeftSon);
    }

5、左旋
在這裏插入圖片描述

 /**
     * 左旋
     *
     * @param node
     */
    private void leftRotate(AVLNode node) {
        AVLNode nParent = node.parent;
        AVLNode nRightSon = node.right;
        AVLNode nRightLeftGrandSon = nRightSon.left;


        if (nParent == null) {
            //更改更改根節點
            this.root = nRightSon;
            //1、更改父節點指向
        } else if (node == nParent.right) {
            nParent.right = nRightSon;
        } else if (node == nParent.left) {
            nParent.left = nRightSon;
        }


        //2、更改右子節點指向
        nRightSon.parent = node;
        nRightSon.left = node;

        //3、更改當前節點指向
        node.parent = nRightSon;
        node.right = nRightLeftGrandSon;

        //4、更改右左節點指向
        if (nRightLeftGrandSon != null) {
            nRightLeftGrandSon.parent = node;
        }

        //此時改變平衡度/深度其實就右子節點和當前節點
        //需要先計算當前節點的深度和平衡度,
        // 因爲原來的右子節點變成當前節點的父節點
        node.depth = calculateDepth(node);
        node.balance = calculateBalance(node);

        nRightSon.depth = calculateDepth(nRightSon);
        nRightSon.balance = calculateBalance(nRightSon);

    }

6、插入數據方法;

 public boolean insert(int data) {
        return insert(root, data);
    }

    /**
     * 插入平衡樹數據
     *
     * @param node 當前數值比較的節點
     * @param data 數值
     */
    public boolean insert(AVLNode node, int data) {
        if (node == null) {
            node = new AVLNode(data);
            root = node;
            return true;
        }
        //插入相同數值的二叉樹
        if (data == node.data) {
            return false;
        }
        //二叉樹左邊數值小,右邊數值大【根據這個特性對數據進行遞歸插入】
        //當插入數據小於當前比較的節點時候,從左邊遞歸插入
        if (data < node.data) {
            //由於左邊 還有數值,需要和下一個左邊值比較
            if (node.left != null) {
                insert(node.left, data);
            } else {
                //左邊數值爲空,可以直接插入
                //將比較節點左邊指向 新節點
                node.left = new AVLNode(data);
                //給新節點parent的父節點參數指向 比較節點
                node.left.parent = node;
            }
        } else {
            //當插入數據大於當前比較的節點時候,從右邊遞歸插入
            if (node.right != null) {
                //由於右邊 還有數值,需要和下一個右邊邊值比較
                insert(node.right, data);

            } else {
                //左邊數值爲空,可以直接插入
                //將比較節點左邊指向 新節點
                node.right = new AVLNode(data);
                node.right.parent = node;
            }
        }
        //更新當前節點上面的balance和depth,並作出旋轉
        rebuild(node);
        return true;
    }
     /**
     * 更新當前節點上面的balance和depth,並作出旋轉
     *
     * @param node
     */
    private void rebuild(AVLNode node) {
        //獲取當前節點的平衡度
        node.balance = calculateBalance(node);
        //此時左樹更改,且大於最大平衡度,應該右旋
        if (node.balance >= MAX_LEFT) {
            //此時左子節點的右子樹更高需要,先左旋
            if (node.left.balance == RIGHT) {
                leftRotate(node.left);
            }
            rightRotate(node);
        }
        //此時右樹更改,且大於最大平衡度,應該左旋
        if (node.balance <= MAX_RIGHT) {
            //此時右子節點的左子樹更高需要,先右旋
            if (node.right.balance == LEFT) {
                rightRotate(node.right);
            }
            leftRotate(node);
        }
        node.balance = calculateBalance(node);
        node.depth = calculateDepth(node);
    }

查看完整代碼:

public class AVLTree {

    private AVLNode root;
    private final int LEFT = 1;
    private final int RIGHT = -1;
    private final int MAX_LEFT = 2;
    private final int MAX_RIGHT = -2;


    public boolean insert(int data) {
        return insert(root, data);
    }

    /**
     * 插入平衡樹數據
     *
     * @param node 當前數值比較的節點
     * @param data 數值
     */
    public boolean insert(AVLNode node, int data) {
        if (node == null) {
            node = new AVLNode(data);
            root = node;
            return true;
        }
        //插入相同數值的二叉樹
        if (data == node.data) {
            return false;
        }
        //二叉樹左邊數值小,右邊數值大【根據這個特性對數據進行遞歸插入】
        //當插入數據小於當前比較的節點時候,從左邊遞歸插入
        if (data < node.data) {
            //由於左邊 還有數值,需要和下一個左邊值比較
            if (node.left != null) {
                insert(node.left, data);
            } else {
                //左邊數值爲空,可以直接插入
                //將比較節點左邊指向 新節點
                node.left = new AVLNode(data);
                //給新節點parent的父節點參數指向 比較節點
                node.left.parent = node;
            }
        } else {
            //當插入數據大於當前比較的節點時候,從右邊遞歸插入
            if (node.right != null) {
                //由於右邊 還有數值,需要和下一個右邊邊值比較
                insert(node.right, data);

            } else {
                //左邊數值爲空,可以直接插入
                //將比較節點左邊指向 新節點
                node.right = new AVLNode(data);
                node.right.parent = node;
            }
        }
        //更新當前節點上面的balance和depth,並作出旋轉
        rebuild(node);
        return true;
    }

    /**
     * 更新當前節點上面的balance和depth,並作出旋轉
     *
     * @param node
     */
    private void rebuild(AVLNode node) {
        //獲取當前節點的平衡度
        node.balance = calculateBalance(node);
        //此時左樹更改,且大於最大平衡度,應該右旋
        if (node.balance >= MAX_LEFT) {
            //此時左子節點的右子樹更高需要,先左旋
            if (node.left.balance == RIGHT) {
                leftRotate(node.left);
            }
            rightRotate(node);
        }
        //此時右樹更改,且大於最大平衡度,應該左旋
        if (node.balance <= MAX_RIGHT) {
            //此時右子節點的左子樹更高需要,先右旋
            if (node.right.balance == LEFT) {
                rightRotate(node.right);
            }
            leftRotate(node);
        }
        node.balance = calculateBalance(node);
        node.depth = calculateDepth(node);
    }

    /**
     * 計算當前節點深度
     *
     * @param node
     * @return
     */
    private int calculateDepth(AVLNode node) {
        int depth = 0;
        if (node.left != null) {
            depth = node.left.depth;
        }
        if (node.right != null && node.right.depth > depth) {
            depth = node.right.depth;
        }
        depth++;
        return depth;
    }

    /**
     * 右旋
     *
     * @param node
     */
    private void rightRotate(AVLNode node) {
        AVLNode nParent = node.parent;
        AVLNode nLeftSon = node.left;
        AVLNode nLeftRightGrandSon = nLeftSon.right;


        if (nParent == null) {
            //更改更改根節點
            this.root = nLeftSon;
        }
        //1、更改父節點指向
        //在操作的時候只改變指針指向,故可以用==
        else if (node == nParent.left) {
            nParent.left = nLeftSon;
        } else if (node == nParent.right) {
            nParent.right = nLeftSon;
        }


        //2、更改左子節點指針指向(父節點和右子節點改變)
        nLeftSon.parent = nParent;
        nLeftSon.right = node;

        //3、更改當前節點
        node.parent = nLeftSon;
        node.left = nLeftRightGrandSon;

        //4、更改右子節點
        if (nLeftRightGrandSon != null) {
            nLeftRightGrandSon.parent = node;
        }

        //此時改變平衡度/深度其實就左子節點和當前節點
        //需要先計算當前節點的深度和平衡度,
        // 因爲原來的左子節點變成當前節點的父節點
        node.depth = calculateDepth(node);
        node.balance = calculateBalance(node);

        nLeftSon.depth = calculateDepth(nLeftSon);
        nLeftSon.balance = calculateBalance(nLeftSon);
    }

    /**
     * 左旋
     *
     * @param node
     */
    private void leftRotate(AVLNode node) {
        AVLNode nParent = node.parent;
        AVLNode nRightSon = node.right;
        AVLNode nRightLeftGrandSon = nRightSon.left;


        if (nParent == null) {
            //更改更改根節點
            this.root = nRightSon;
            //1、更改父節點指向
        } else if (node == nParent.right) {
            nParent.right = nRightSon;
        } else if (node == nParent.left) {
            nParent.left = nRightSon;
        }


        //2、更改右子節點指向
        nRightSon.parent = node;
        nRightSon.left = node;

        //3、更改當前節點指向
        node.parent = nRightSon;
        node.right = nRightLeftGrandSon;

        //4、更改右左節點指向
        if (nRightLeftGrandSon != null) {
            nRightLeftGrandSon.parent = node;
        }

        //此時改變平衡度/深度其實就右子節點和當前節點
        //需要先計算當前節點的深度和平衡度,
        // 因爲原來的右子節點變成當前節點的父節點
        node.depth = calculateDepth(node);
        node.balance = calculateBalance(node);

        nRightSon.depth = calculateDepth(nRightSon);
        nRightSon.balance = calculateBalance(nRightSon);

    }

    /**
     * 計算平衡因子
     *
     * @param node
     * @return
     */
    private int calculateBalance(AVLNode node) {
        int leftDepth;
        int rightDepth;
        //獲取節點左邊深度
        if (node.left != null) {
            leftDepth = node.left.depth;
        } else {
            leftDepth = 0;
        }

        if (node.right != null) {
            rightDepth = node.right.depth;
        } else {
            rightDepth = 0;
        }
        return leftDepth - rightDepth;
    }


    class AVLNode {
        /**
         * 保存節點的數據
         */
        private int data;
        /**
         * 當前節點的深度
         * 深度是常用參數,保存深度數據,
         * 不用多次遞歸去計算節點的深度
         */
        private int depth;

        /**
         * 平衡指數(左邊深度-右邊深度)
         * 左子樹高則爲正,左邊子樹第則爲負數
         */
        private int balance;
        /**
         * 指向父節點(簡化後面關於循環父節點獲取)
         */
        public AVLNode parent;

        /**
         * 左子樹
         */
        public AVLNode left;

        /**
         * 右子樹
         */
        public AVLNode right;

        public AVLNode(int data) {
            this.data = data;
            depth = 1;
            balance = 0;
            left = null;
            right = null;
        }
    }

    public static void main(String[] args) {

        AVLTree node = new AVLTree();
       // com.yin.dynamic_elasticjob.util.AVLNode node = new com.yin.dynamic_elasticjob.util.AVLNode(3);
        node.insert(3);
        node.insert(2);
        node.insert(1);
        node.insert(4);
        node.insert(5);
        node.insert(6);
        node.insert(7);
        node.insert(10);
        node.insert(9);

        System.out.println("-----驗證是否平衡-----");



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