算法其實很簡單—平衡二叉樹的構建

目錄

 

1. 基本概念

2. 整體思路

3. 代碼實現


1. 基本概念

平衡二叉樹的本質其實也是二叉排序樹,具體可參考:

算法其實很簡單—二叉排序樹的構建

算法其實很簡單—二叉排序樹的刪除

平衡二叉樹的特點是任意節點的子樹的高度差都小於等於1。

2. 整體思路

平衡二叉樹的構建基本分爲三種情況,左旋轉、右旋轉、雙旋轉

1、當根節點的右子樹的高度比左子樹高度大於1時,即高度相差2以上,則進行左旋轉,思路如下圖:

 

2、當根節點的左子樹的高度比右子樹高度大於1時,則進行右旋轉:

 

3、如下圖,複合右旋轉條件,但是右旋轉後還不是平衡二叉樹,則分以下兩種情況

3.1 當符合右旋條件,並且根節點的左子節點的右子樹高度大於左子樹的時候,先進行一次左旋轉

3.2 當符合左旋轉條件,並且根節點的右子樹的左子樹節點大於右子樹節點的時候,先進行一次右旋轉

 

3. 代碼實現

/**
 * @author 浪子傑
 * @version 1.0
 * @date 2020/6/2
 */
public class AVLTreeDemo {
    public static void main(String[] args) {
//        int[] arr = {4, 3, 6, 5, 7, 8};
//        int[] arr = {10, 12, 8, 9, 7, 6};
        int[] arr = {10, 11, 7, 6, 8, 9};
        AVLTree avlTree = new AVLTree();
        for (int i : arr) {
            avlTree.add(new Node(i));
        }

        System.out.println(avlTree.root.height());
        System.out.println(avlTree.root.leftHeight());
        System.out.println(avlTree.root.rightHeight());
    }
}

class Node {

    int value;
    Node left;
    Node right;

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

    /**
     * 左旋轉
     */
    public void leftRotate() {
        // 創建一個新的節點,值爲當前節點的值
        Node newNode = new Node(value);
        // 把新節點的左子樹設爲當前節點的左子樹
        newNode.left = left;
        // 把新節點的右子樹設爲當前右子節點的左子樹
        newNode.right = right.left;
        // 把當前節點的值換位右子節點的值
        this.value = right.value;
        // 把當前節點的右子樹設爲右子節點的右子樹
        right = right.right;
        // 把當前節點的左子樹設爲新節點
        left = newNode;
    }

    /**
     * 右旋轉
     */
    public void rightRotate() {
        // 創建一個新的節點並將當前值賦給新節點
        Node newNode = new Node(value);
        // 把新節點的右子樹設爲當前節點的右子樹
        newNode.right = right;
        // 把新節點的左子樹設爲當前節點左子節點的右子樹
        newNode.left = left.right;
        // 把當前值設爲左子樹節點的值
        this.value = left.value;
        // 把當前節點的左子樹設爲左子節點的左子樹
        this.left = left.left;
        // 把當前節點的右子樹設爲新節點
        this.right = newNode;


    }

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

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

    /**
     * 獲取以當前節點爲根節點的樹的高度
     *
     * @return
     */
    public int height() {
        return Math.max(left == null ? 0 : left.height(), right == null ? 0 : right.height()) + 1;
    }

    /**
     * 查詢要刪除的節點
     *
     * @param value
     * @return
     */
    public Node search(int value) {
        // 如果要查找的值==當前節點的值,返回當前節點
        // 如果要查找的值 < 當前節點,則向該節點的左子樹查找
        // 否則向該節點的右子樹查找
        if (this.value == value) {
            return this;
        } else if (value < this.value) {
            // 如果該節點的左子節點爲null,直接返回null
            if (this.left == null) {
                return null;
            }
            return this.left.search(value);
        } else {
            if (this.right == null) {
                return null;
            }
            return this.right.search(value);
        }
    }

    /**
     * 查找要刪除節點的父節點
     *
     * @param value
     * @return
     */
    public Node searchParent(int value) {
        // 如果當前節點的子節點不爲空,並且當前節點子節點的值 == value,則返回當前節點
        // 如果當前節點的左子節點不爲null,並且當前節點的值 > value,則向該節點的左子節點遍歷
        // 如果當前節點的右子節點不爲null,並且當前節點的值不> value,則向該節點的右子節點遍歷
        // 否則沒有找到,返回null
        if ((this.left != null && this.left.value == value) ||
                (this.right != null && this.right.value == value)) {
            return this;
        } else if (this.left != null && this.value > value) {
            return this.left.searchParent(value);
        } else if (this.right != null && this.value <= value) {
            return this.right.searchParent(value);
        } else {
            return null;
        }
    }

    /**
     * 添加節點
     *
     * @param node
     */
    public void add(Node node) {
        // 如果該節點爲null,直接返回
        if (node == null) {
            return;
        }
        // add的節點小於當前節點,說明應該在當前節點的左邊
        // 否則放在當前節點的右邊
        if (node.value < this.value) {
            // 如果當前節點的左邊沒有子節點,則直接把add節點放在當前節點的左子節點
            // 否則的話,遍歷當前左子節點,直到找到合適位置
            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,則進行左旋轉
        // 如果當前節點的左節點比右節點的高度 > 1,則進行右旋轉
        if (rightHeight() - leftHeight() > 1) {
            // 如果當前節點的右子節點的左子樹高度大於右子樹的高度,則先進行右旋轉
            if (right != null && right.leftHeight() > right.rightHeight()) {
                right.leftRotate();
            }
            leftRotate();
        } else if (leftHeight() - rightHeight() > 1) {
            // 如果當前節點的左子節點的右子樹高度大於左子樹的高度,則先進行左旋轉
            if (left != null && left.rightHeight() > left.leftHeight()) {
                left.leftRotate();
            }
            rightRotate();
        }

    }

    /**
     * 中序遍歷
     */
    public void middleOrder() {
        if (this.left != null) {
            this.left.middleOrder();
        }
        System.out.println(this);
        if (this.right != null) {
            this.right.middleOrder();
        }
    }

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

class AVLTree {

    Node root;

    /**
     * 刪除節點
     *
     * @param value
     */
    public void delete(int value) {
        // 如果父節點爲null,直接返回
        if (root == null) {
            return;
        } else {
            Node targetNode = search(value);
            // 如果要刪除的節點爲null,直接返回
            if (targetNode == null) {
                return;
            }
            // 如果父節點的左右節點都爲null,說明只有一個節點,直接將root設爲null即可
            if (root.left == null && root.right == null) {
                root = null;
                return;
            }
            Node parentNode = searchParent(value);
            // 如果要刪除節點的左右節點都爲null,說明該要刪除的節點爲子節點
            if (targetNode.left == null && targetNode.right == null) {
                // 如果父節點的左子節點不爲null並且是要刪除的節點,則將父節點的左子節點設爲null
                // 如果父節點的右子節點不爲null並且是要刪除的節點,則將父節點的右子節點設爲null
                if (parentNode.left != null && parentNode.left.value == value) {
                    parentNode.left = null;
                } else if (parentNode.right != null && parentNode.right.value == value) {
                    parentNode.right = null;
                }
            } else if (targetNode.left != null && targetNode.right != null) {
                // 如果要刪除的左右子節點都不爲null,則查找要刪除節點右子節點的最小值,刪除最小節點並將值賦給要刪除節點
                int treeMin = delRightTreeMin(targetNode.right);
                System.out.println("最小的爲---" + treeMin);
                targetNode.value = treeMin;
            } else {
                // 如果要刪除的節點左右子節點有一個爲null
                // 如果要刪除的子節點爲root節點
                if (parentNode == null) {
                    // 如果左子節點不爲null,則將左子節點賦給root
                    // 否則將右子節點賦給root
                    if (targetNode.left != null) {
                        root = targetNode.left;
                    } else {
                        root = targetNode.right;
                    }
                } else if (parentNode.left.value == value) {
                    // 如果要刪除的節點爲parentNode的左子節點
                    // 如果要刪除的節點的左子節點不爲null,則將parentNode的左子節點指向要刪除節點的左子節點
                    // 否則則指向要刪除節點的右子節點
                    if (targetNode.left != null) {
                        parentNode.left = targetNode.left;
                    } else {
                        parentNode.left = targetNode.right;
                    }
                } else {
                    if (targetNode.right != null) {
                        parentNode.right = targetNode.right;
                    } else {
                        parentNode.right = targetNode.left;
                    }
                }

            }
        }
    }

    /**
     * 返回以node節點爲根節點的二叉排序樹的最小值
     *
     * @param node
     * @return
     */
    public int delRightTreeMin(Node node) {
        Node target = node;
        while (target.left != null) {
            target = target.left;
        }
        delete(target.value);
        return target.value;
    }

    /**
     * 查找要刪除的節點
     *
     * @param value
     * @return
     */
    public Node search(int value) {
        if (root == null) {
            return null;
        } else {
            return root.search(value);
        }
    }

    /**
     * 查詢要刪除的父節點
     *
     * @param value
     * @return
     */
    public Node searchParent(int value) {
        if (root == null) {
            return null;
        } else {
            return root.searchParent(value);
        }
    }

    /**
     * 添加子節點
     *
     * @param node
     */
    public void add(Node node) {
        if (root == null) {
            root = node;
        } else {
            root.add(node);
        }
    }

    /**
     * 中序遍歷
     */
    public void infixOrder() {
        if (root != null) {
            root.middleOrder();
        } else {
            System.out.println("當前root爲空");
        }
    }
}

 

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