美文网首页
手写平衡二叉树,实现插入、左右平衡功能

手写平衡二叉树,实现插入、左右平衡功能

作者: jqboooo | 来源:发表于2018-12-21 17:36 被阅读0次

    1、概念

    平衡二叉树(Self-Balancing Binary Search Tree 或 Height-Balancing Binary Search Tree):是一种二叉排序树,其中每一个节点的左子树和右子树的高度差至多等于1。

    如下图比较标准的平衡二叉树:


    1.png

    主要记住三个概念:

    1. 平衡因子: 二叉树上节点的左子树深度减去右子树深度的值称为平衡因子BF(Balance Factor)
    2. 左旋: 是指左子树的高度比右子树的高度小于2,就要经过左旋转,来达到平衡。
    3. 右旋: 是指右子树的高度比左子树的高度小于2,就要经过右旋转,来达到平衡。

    2、例图分析

    最小平衡树:距离插入节点最近的,且平衡因子的绝对值大于 1 的节点为根的子树,我们称为最小不平衡子树

    2.png

    左旋例图

    3.png

    右旋例图

    4.png

    左右平衡

    5.png

    3、代码实现(代码注释有详细的左平衡、右平衡的规则)

    import android.support.annotation.NonNull;
    import java.util.LinkedList;
    
    /**
    * 平衡二叉树
    *
    * author: bobo
    * create time: 2018/12/18 11:55 AM
    * email: jqbo84@163.com
    */
    public class AVLBTree<E> {
    
        private static final int LH = 1;
        private static final int RH = -1;
        private static final int EH = 0;
    
        private Node<E> root;
    
        private int size;
    
        public Node<E> getRoot() {
            return root;
        }
    
        public int getSize() {
            return size;
        }
    
        /**
         * 左旋转
         * @param x
         */
        public void leftRotate(Node<E> x){
            if(x == null){
                return;
            }
            // 1. 先取到 y 结点
            Node<E> y = x.rightChild;
            // 2. 把𝞫作为X的右孩子
            x.rightChild = y.leftChild;
            // 3. 判断y的左结点是否为null
            if(y.leftChild != null){
                y.leftChild.parent = x;
            }
            // 4. 把x的父结点赋给y的父结点
            y.parent = x.parent;
            // 5. 判断x的结点是否为root结点
            if(x.parent == null){
                root = y;
            } else {
                if(x.parent.leftChild == x){
                    x.parent.leftChild = y;
                } else {
                    x.parent.rightChild = y;
                }
            }
            // 6. 把x结点作为y的左结点
            y.leftChild = x;
            x.parent = y;
        }
    
        /**
         * 右旋转
         * @param x
         */
        public void rightRotate(Node<E> x){
            if(x == null){
                return;
            }
            Node<E> y = x.leftChild;
            x.leftChild = y.rightChild;
            if(y.rightChild != null){
                y.rightChild.parent = x;
            }
            y.parent = x.parent;
            if(x.parent == null){
                root = y;
            } else {
                if(x.parent.leftChild == x){
                    x.parent.leftChild = y;
                } else {
                    x.parent.rightChild = y;
                }
            }
            y.rightChild = x;
            x.parent = y;
        }
    
        /**
         * 左平衡操作,即结点t的不平衡是因为左子树过深
         *
         * 1、如果新的结点插入到t的左孩子的左子树中,则直接进行右旋操作即可
         *              t                                       tl
         *             /  \           右旋操作              /        \
         *            tl   tr       ------------->        tll          t
         *           /  \                                /  \        /   \
         *          tll  tlr                          lcll  lclr   tlr   tr
         *         /   \
         *      lcll   lclr
         *
         * 2、如果新的结点插入到t的左孩子的右子树中,则需要进行分情况讨论
         *
         *  情况a:当t的左孩子的右子树根节点的balance = RIGHT_HIGH  -1
         *          t                       t                           tlr
         *         /  \                    /  \                        /  \
         *        tl   6      左旋       tlr   6        右旋           tl    t
         *       /  \       ------->     /  \        -------->       /    /  \
         *      3  tlr                 tl    5                      3    5    6
         *            \                /
         *             5              3
         *  情况b:当t的左孩子的右子树根节点的balance = LEFT_HIGH  1
         *
         *          t                       t                          tlr
         *         /  \                    /  \                        /  \
         *        tl    6     左旋        tlr    6        右旋         tl    t
         *        /  \      ------->     /           -------->       / \    \
         *       3  tlr                tl                           3   5    6
         *          /                 / \
         *         5                 3   5
         *
         *  情况c:当t的左孩子的右子树根节点的balance = EQUAL_HIGH  0
         *
         *          t                       t                           tlr
         *         /  \                    /  \                        /   \
         *        tl   7     左旋         tlr    7        右旋         tl    t
         *        /  \      ------->     / \         -------->       / \   / \
         *       3   tlr                tl   6                      3  5  6   7
         *           / \               / \
         *          5   6             3   5
         */
        public void leftBalance(Node<E> t){
            if(t == null){
                return;
            }
            Node<E> tl = t.leftChild;
            switch (tl.balance){
                case LH://1.新的结点插入到node的左结点的左子树中
                    rightRotate(t);
                    t.balance = EH;
                    tl.balance = EH;
                    break;
                case RH://2.新的结点插入到node的左结点的右子树中
                    Node<E> tlr = tl.rightChild;
                    switch (tlr.balance){
                        case LH:
                            t.balance = RH;
                            tl.balance = EH;
                            tlr.balance = EH;
                            break;
                        case RH:
                            t.balance = EH;
                            tl.balance = LH;
                            tlr.balance = EH;
                            break;
                        case EH:
                            t.balance = EH;
                            tl.balance = EH;
                            tlr.balance = EH;
                            break;
                        default:
                            break;
                    }
                    leftRotate(tl);
                    rightRotate(t);
                    break;
            }
        }
    
        /**
         * 右平衡操作,即结点t的不平衡是因为右子树过深
         *
         * 1、如果新的结点插入到t的右孩子的右子树中,则直接进行左旋操作即可
         *
         *          t                                             tr
         *        /   \                                        /     \
         *       l     tr              左旋操作                t       rr
         *           /   \          ----------->             / \     /   \
         *          rl    rr                                l  rl  rrl    rrr
         *              /   \
         *           rrl    rrr
         *
         * 2、如果新的结点插入到t的右孩子的左子树中,则需要进行分情况讨论
         *  情况a:当t的右孩子的左子树根节点的balance = LEFT_HIGH     1
         *
         *          t                       t                           trl
         *         /  \                    /  \                        /   \
         *        2   tr        tr右旋      2  trl        t左旋       t    tr
         *            /  \     ------->      /  \       ------->    /  \     \
         *          trl   5                 6   tr                 2    6     5
         *          /                            \
         *         6                              5
         * 情况b:当t的右孩子的左子树根节点的balance = RIGHT_HIGH    -1
         *
         *          t                       t                           trl
         *         /  \                    /  \                        /   \
         *        2   tr        tr右旋      2  trl         t左旋        t    tr
         *            /  \     ------->        \        ------->     /     /  \
         *           trl  5                     tr                  2     6   5
         *            \                        /  \
         *             6                      6    5
         *
         * 情况C:当t的右孩子的左子树根节点的balance = EQUAL_HIGH    0
         *          t                       t                           trl
         *         /  \                    /  \                        /   \
         *        2   tr         右旋     2    trl         左旋         t    tr
         *            /  \     ------->       /  \      ------->     / \   / \
         *         trl    5                  6   tr                 2   6  7   5
         *          / \                          /  \
         *         6   7                        7    5
         *
         */
        public void rightBalance(Node<E> t){
            if(t == null){
                return;
            }
            Node<E> tr = t.rightChild;
            switch (tr.balance){
                case RH: //1.新的结点插入到node的右结点的右子树中
                    leftRotate(t);
                    t.balance = EH;
                    tr.balance = EH;
                    break;
                case LH: //2.新的结点插入到node的右结点的左子树中
                    Node<E> trl = tr.leftChild;
                    switch (trl.balance){
                        case RH:
                            t.balance = LH;
                            tr.balance = EH;
                            trl.balance = EH;
                            break;
                        case LH:
                            t.balance = EH;
                            tr.balance = RH;
                            trl.balance = EH;
                            break;
                        case EH:
                            t.balance = EH;
                            tr.balance = EH;
                            trl.balance = EH;
                            break;
                        default:
                            break;
                    }
                    rightRotate(tr);
                    leftRotate(t);
                    break;
            }
        }
    
        /**
         * 往平衡二叉树插入一个结点
         * @param element
         */
        public boolean insertElement(E element){
            Node<E> t = root;
            if(t == null){
                root = new Node<>(element, null);
                root.balance = 0;
                size = 1;
                return true;
            } else {
                int cmp = 0;
                Node<E> parent = null;
                Comparable<E> e = (Comparable<E>) element;
                //1. 查找可以插入的位置
                do {
                    parent = t;
                    cmp = e.compareTo(t.element);
                    if(cmp < 0){
                        t = t.leftChild;
                    } else if (cmp > 0){
                        t = t.rightChild;
                    } else {
                        return false;
                    }
                } while (t != null);
                //2. 查找结束,现在插入
                Node<E> child = new Node<>(element, parent);
                if(cmp < 0){
                    parent.leftChild = child;
                } else {
                    parent.rightChild = child;
                }
                //3. 插入结束,要开始检查平衡因子,回溯往回找,平衡因子绝对值是否等于2
                while (parent != null){
                    cmp = e.compareTo(parent.element);
                    if(cmp < 0){
                        parent.balance++;
                    } else if(cmp > 0){
                        parent.balance--;
                    }
                    if (parent.balance == 0){//如果插入后还是平衡树,不用调整
                        break;
                    }
                    if(parent.balance == 2 || parent.balance == -2){
                        //出现了平衡的问题,需要修正
                        fixAfterInsertion(parent);
                        break;
                    } else {
                        parent = parent.parent;
                    }
                }
                size++;
            }
    
            return true;
        }
    
        /**
         * 修正插入结点后的平衡二叉树
         * @param parent
         */
        private void fixAfterInsertion(Node<E> parent) {
            if(parent.balance == 2){
                leftBalance(parent);
            }
            if(parent.balance == -2){
                rightBalance(parent);
            }
        }
    
        /**
         * 一层一层打印出数据
         * @param root)
         */
        public void showAVL(Node<E> root){
            if(root == null){
                return;
            }
            java.util.LinkedList<Node<E>> list = new java.util.LinkedList<>();
            list.offer(root);
            while (!list.isEmpty()){
                Node<E> node = list.pop();
                System.out.println("node.element = " + node.element);
                if(node.leftChild != null){
                    list.offer(node.leftChild);
                }
                if(node.rightChild != null){
                    list.offer(node.rightChild);
                }
            }
        }
    
        public class Node<E> implements Comparable<Node<E>> {
            E element;
            Node<E> leftChild;
            Node<E> rightChild;
            Node<E> parent;
    
            int balance = 0;//平衡因子
    
            public Node(E element, Node<E> parent) {
                this.element = element;
                this.parent = parent;
            }
    
            @Override
            public int compareTo(@NonNull Node<E> o) {
                if(this.balance > o.balance){
                    return -1;
                } else if(this.balance < o.balance){
                    return 1;
                }
                return 0;
            }
        }
    }
    

    4、测试

    @Test
    public void testAVLBTree(){
        Integer[] nums = {5,8,2,0,1,-2};
        AVLBTree<Integer> tree = new AVLBTree<>();
        for (int i = 0; i < nums.length; i++) {
            Integer num = nums[i];
            tree.insertElement(num);
        }
        tree.showAVL(tree.getRoot());
    }
    
    打印结果:
    node.element = 1
    node.element = 0
    node.element = 5
    node.element = -2
    node.element = 2
    node.element = 8
    

    如下测试结果图:

    6.jpg

    相关文章

      网友评论

          本文标题:手写平衡二叉树,实现插入、左右平衡功能

          本文链接:https://www.haomeiwen.com/subject/qidvkqtx.html