前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >平衡二叉树AVLTree手写实现(Java)

平衡二叉树AVLTree手写实现(Java)

作者头像
aruba
发布2021-12-06 17:08:01
3670
发布2021-12-06 17:08:01
举报
文章被收录于专栏:android技术

定义节点:

代码语言:javascript
复制
        //平衡因子 左树深度多
        public static final int LH = 1;
        //平衡因子 右数深度多
        public static final int RH = -1;
        //平衡因子 左右树深度相同
        public static final int EH = 0;

        class AVLNode<T> {
            T value;
            AVLNode<T> leftChild;
            AVLNode<T> rightChild;
            AVLNode<T> parent;
            int balance = EH;
        }

实现左旋方法:

左旋.jpg

代码语言:javascript
复制
        /**
         * 节点左旋
         * //            转换前:
         * //                |
         * //                a   <-
         * //               / \
         * //              b   c
         * //                  / \
         * //                 z  h
         * //
         * //            转换后:
         * //
         * //                |
         * //                c
         * //               / \
         * //         ->   a   h
         * //             / \
         * //            b  z
         */
        private void leftRotate(AVLNode<T> a) {
            AVLNode<T> c = a.rightChild;

            //1.a的父节点的孩子指向c,c的父节点变为a的父节点
            if (a.parent == null) {//根节点,即a没有父节点
                root = c;
            } else if (a.parent.leftChild == a) {//a是左孩子
                a.parent.leftChild = c;
            } else {//a是右孩子
                a.parent.rightChild = c;
            }
            //c的父节点变为a的父节点
            c.parent = a.parent;

            //2.a的右孩子变为z
            AVLNode<T> z = c.leftChild;
            a.rightChild = z;
            //z父节点指向a
            if (z != null)
                z.parent = a;

            //3.c的左孩子变为a,a的父节点指向c
            c.leftChild = a;
            //a的父节点指向c
            a.parent = c;
        }

实现右旋方法:

右旋.jpg

代码语言:javascript
复制
        /**
         * 节点右旋
         * //            转换前:
         * //                |
         * //                a   <-
         * //               / \
         * //              b   c
         * //             / \
         * //            z  h
         * //
         * //            转换后:
         * //
         * //                |
         * //                b
         * //               / \
         * //              z   a  <-
         * //                 / \
         * //                h  c
         */
        private void rightRotate(AVLNode<T> a) {
            AVLNode<T> b = a.leftChild;

            //1.b的父节点变为a的父节点,a的父节点的孩子变为b
            b.parent = a.parent;
            //a的父节点的孩子变为b
            if (a.parent == null) {//根节点,即a没有父节点
                root = b;
            } else if (a.parent.leftChild == a) {//a是左孩子
                a.parent.leftChild = b;
            } else {
                a.parent.rightChild = b;
            }

            //2.a的左孩子变为h,h父节点指向a
            AVLNode<T> h = b.rightChild;
            a.leftChild = b.leftChild;
            if (h != null) {
                h.parent = a;
            }

            //3.b的右孩子指向a,a的父节点变为b
            b.rightChild = a;
            //a的父节点变为b
            a.parent = b;
        }

实现左平衡和右平衡:

代码语言:javascript
复制
        /**
         * 右平衡
         */
        public void rightBalance(AVLNode<T> t) {
            AVLNode<T> tr = t.rightChild;

            switch (tr.balance) {
                case RH:
//            1、如果新的结点插入到t的右孩子的右子树中,则直接进行左旋操作即可
//              t                                            tr
//            /   \                                        /     \
//           l     tr              左旋操作                   t       trr
//                /   \         ----------->             / \     /    \
//              trl    trr                             l   trl  rrl    rrr
//                    /   \
//                  rrl     rrr  <----插入rrl或rrr
                    leftRotate(t);
                    t.balance = EH;
                    tr.balance = EH;
                    //trr平衡因子不变
                    break;
                case LH://2、如果新的结点插入到p的右孩子的左子树中,则需要进行分情况讨论
                    switch (tr.leftChild.balance) {
                        case LH:   //情况a:当p的右孩子的左子树根节点的balance = LEFT_HIGH
                            //          t                       t                           trl
                            //         /  \                    /  \                        /   \
                            //        2   tr        tr右旋      2  trl         t左旋          t     tr
                            //            /  \     ------->       /  \      ------->     /  \     \
                            //        trl     5                  6   tr                 2   6      5
                            //       /                             \
                            //      6   <----插入                  5
                            t.balance = EH;
                            tr.balance = RH;
                            tr.leftChild.balance = EH;
                            break;
                        case RH://情况b:当p的右孩子的左子树根节点的balance = RIGHT_HIGH
                            //          t                       t                           trl
                            //         /  \                    /  \                        /   \
                            //        2   tr        tr右旋      2  trl         t左旋          t    tr
                            //            /  \     ------->          \      ------->     /     /  \
                            //           trl  5                      tr                 2     6    5
                            //            \                          /  \
                            //             6    <----插入         6    5
                            t.balance = LH;
                            tr.balance = EH;
                            tr.leftChild.balance = EH;
                            break;
                        case EH://情况C:当p的右孩子的左子树根节点的balance = EQUAL_HIGH
                            //          t                       t                           trl
                            //            \                       \                        /   \
                            //            tr        tr右旋         trl        t左旋       t    tr
                            //           /         ------->         \       -------> 
                            //         trl  <----插入             tr                  
                            t.balance = EH;
                            tr.balance = EH;
                            tr.leftChild.balance = EH;
                            break;
                    }

                    rightRotate(t.rightChild);
                    leftRotate(t);
                    break;
                case EH:
                    break;
            }
        }

        /**
         * 左平衡
         */
        public void leftBalance(AVLNode<T> t) {
            AVLNode<T> tl = t.leftChild;

            switch (tl.balance) {
                case LH://1、如果新的结点插入到t的左孩子的左子树中,则直接进行右旋操作即可
//                  t                                      tl
//                 /  \         t右旋操作                  /         \
//                tl   tr       ------------->        tll         t
//               /  \                                /  \        /   \
//              tll  tlr                           lcll  lclr    tlr   tr 
//              /  \
//            lcll   lclr <----插入lcll或lclr
                    rightRotate(t);
                    t.balance = EH;
                    tl.balance = EH;
                    //tll平衡因子不变
                    break;
                case RH://2、如果新的结点插入到t的左孩子的右子树中,则需要进行分情况讨论
                    switch (tl.rightChild.balance) {
                        case RH://情况a:当t的左孩子的右子树根节点的balance = RIGHT_HIGH
                            //          t                       t                           tlr
                            //         /  \                    /  \                        /  \
                            //        tl   6      tl左旋     tlr    6       t右旋             tl    t
                            //       /  \       ------->     /  \        -------->       /    /  \
                            //      3  tlr                 tl    5                      3    5    6
                            //            \                /
                            //             5  <----插入     3
                            t.balance = EH;
                            tl.balance = LH;
                            tl.rightChild.balance = EH;
                            break;
                        case LH://情况b:当t的左孩子的右子树根节点的balance = LEFT_HIGH
                            //          t                       t                          tlr
                            //         /  \                    /  \                        /  \
                            //       tl    6      tl左旋      tlr    6          t右旋        tl    t
                            //      /  \        ------->     /              -------->    / \    \
                            //     3  tlr                   tl                          3   5    6
                            //        /                    / \
                            //       5   <----插入          3   5
                            t.balance = RH;
                            tl.balance = EH;
                            tl.rightChild.balance = EH;
                            break;
                        case EH://情况c:当t的左孩子的右子树根节点的balance = EQUAL_HIGH
                            //          t                       t                          tlr
                            //         /                       /                           /  \
                            //        tl            tl左旋    tlr            t右旋         tl   t
                            //          \          ------->   /            -------->              
                            //         tlr  <----插入        tl                                     
                            t.balance = EH;
                            tl.balance = EH;
                            tl.rightChild.balance = EH;
                            break;
                    }

                    leftRotate(t.leftChild);
                    rightRotate(t);
                    break;
                case EH:
                    break;
            }
        }

实现插入元素:

代码语言:javascript
复制
        /**
         * 插入元素
         * @param value
         */
        public void insertNode(T value) {
            if (root == null) {//没有元素
                root = new AVLNode<>(value, null, null, null);
            } else {
                Comparable e = value;
                AVLNode<T> head = root;
                AVLNode<T> parent = null;
                int cmp = 0;
                while (head != null) {
                    cmp = e.compareTo(head.value);

                    parent = head;
                    if (cmp == 0) {
                        return;
                    } else if (cmp > 0) {//往右子树偏移
                        head = head.rightChild;
                    } else {
                        head = head.leftChild;
                    }
                }

                if (cmp > 0) {//往右子树插入节点
                    parent.rightChild = new AVLNode<>(value, null, null, parent);
                } else {
                    parent.leftChild = new AVLNode<>(value, null, null, parent);
                }
                
                //平衡因子调整
                while (parent != null) {
                    if (e.compareTo(parent.value) > 0) {//右子树多了深度
                        parent.balance--;
                    } else {
                        parent.balance++;
                    }

                    if(parent.balance == 0){//插入后正好平衡了,不用处理了
                        break;
                    }
                    
                    if (Math.abs(parent.balance) == 2) {//不平衡了
                        //根据平衡因子进行旋转操作
                        calcBalance(parent);
                        break;
                    } else {//继续往上找父亲,调整平衡因子
                        parent = parent.parent;
                    }
                }
            }

            size++;
        }

        /**
         * 处理平衡问题
         * @param parent
         */
        private void calcBalance(AVLNode<T> parent) {
            if (parent.balance == -2) {//右子树多了
                rightBalance(parent);
            } else {
                leftBalance(parent);
            }
        }

完整代码:

代码语言:javascript
复制
    /**
     * 平衡二叉树
     */
    public static class AVLTree<T extends Comparable<T>> {
        //平衡因子 左树深度多
        public static final int LH = 1;
        //平衡因子 右数深度多
        public static final int RH = -1;
        //平衡因子 左右树深度相同
        public static final int EH = 0;

        //根节点
        AVLNode<T> root;
        
        //元素个数
        private int size = 0;

        public AVLTree() {
        }

        public AVLTree(AVLNode<T> root) {
            this.root = root;
        }

        /**
         * 节点左旋
         * //            转换前:
         * //                |
         * //                a   <-
         * //               / \
         * //              b   c
         * //                  / \
         * //                 z  h
         * //
         * //            转换后:
         * //
         * //                |
         * //                c
         * //               / \
         * //         ->   a   h
         * //             / \
         * //            b  z
         */
        private void leftRotate(AVLNode<T> a) {
            AVLNode<T> c = a.rightChild;

            //1.a的父节点的孩子指向c,c的父节点变为a的父节点
            if (a.parent == null) {//根节点,即a没有父节点
                root = c;
            } else if (a.parent.leftChild == a) {//a是左孩子
                a.parent.leftChild = c;
            } else {//a是右孩子
                a.parent.rightChild = c;
            }
            //c的父节点变为a的父节点
            c.parent = a.parent;

            //2.a的右孩子变为z
            AVLNode<T> z = c.leftChild;
            a.rightChild = z;
            //z父节点指向a
            if (z != null)
                z.parent = a;

            //3.c的左孩子变为a,a的父节点指向c
            c.leftChild = a;
            //a的父节点指向c
            a.parent = c;
        }

        /**
         * 节点右旋
         * //            转换前:
         * //                |
         * //                a   <-
         * //               / \
         * //              b   c
         * //             / \
         * //            z  h
         * //
         * //            转换后:
         * //
         * //                |
         * //                b
         * //               / \
         * //              z   a  <-
         * //                 / \
         * //                h  c
         */
        private void rightRotate(AVLNode<T> a) {
            AVLNode<T> b = a.leftChild;

            //1.b的父节点变为a的父节点,a的父节点的孩子变为b
            b.parent = a.parent;
            //a的父节点的孩子变为b
            if (a.parent == null) {//根节点,即a没有父节点
                root = b;
            } else if (a.parent.leftChild == a) {//a是左孩子
                a.parent.leftChild = b;
            } else {
                a.parent.rightChild = b;
            }

            //2.a的左孩子变为h,h父节点指向a
            AVLNode<T> h = b.rightChild;
            a.leftChild = b.leftChild;
            if (h != null) {
                h.parent = a;
            }

            //3.b的右孩子指向a,a的父节点变为b
            b.rightChild = a;
            //a的父节点变为b
            a.parent = b;
        }

        /**
         * 右平衡
         */
        public void rightBalance(AVLNode<T> t) {
            AVLNode<T> tr = t.rightChild;

            switch (tr.balance) {
                case RH:
//            1、如果新的结点插入到t的右孩子的右子树中,则直接进行左旋操作即可
//              t                                            tr
//            /   \                                        /     \
//           l     tr              左旋操作                   t       trr
//                /   \         ----------->             / \     /    \
//              trl    trr                             l   trl  rrl    rrr
//                    /   \
//                  rrl     rrr  <----插入rrl或rrr
                    leftRotate(t);
                    t.balance = EH;
                    tr.balance = EH;
                    //trr平衡因子不变
                    break;
                case LH://2、如果新的结点插入到p的右孩子的左子树中,则需要进行分情况讨论
                    switch (tr.leftChild.balance) {
                        case LH:   //情况a:当p的右孩子的左子树根节点的balance = LEFT_HIGH
                            //          t                       t                           trl
                            //         /  \                    /  \                        /   \
                            //        2   tr        tr右旋      2  trl         t左旋          t     tr
                            //            /  \     ------->       /  \      ------->     /  \     \
                            //        trl     5                  6   tr                 2   6      5
                            //       /                             \
                            //      6   <----插入                  5
                            t.balance = EH;
                            tr.balance = RH;
                            tr.leftChild.balance = EH;
                            break;
                        case RH://情况b:当p的右孩子的左子树根节点的balance = RIGHT_HIGH
                            //          t                       t                           trl
                            //         /  \                    /  \                        /   \
                            //        2   tr        tr右旋      2  trl         t左旋          t    tr
                            //            /  \     ------->          \      ------->     /     /  \
                            //           trl  5                      tr                 2     6    5
                            //            \                          /  \
                            //             6    <----插入         6    5
                            t.balance = LH;
                            tr.balance = EH;
                            tr.leftChild.balance = EH;
                            break;
                        case EH://情况C:当p的右孩子的左子树根节点的balance = EQUAL_HIGH
                            //          t                       t                           trl
                            //            \                       \                        /   \
                            //            tr        tr右旋         trl        t左旋       t    tr
                            //           /         ------->         \       -------> 
                            //         trl  <----插入             tr                  
                            t.balance = EH;
                            tr.balance = EH;
                            tr.leftChild.balance = EH;
                            break;
                    }

                    rightRotate(t.rightChild);
                    leftRotate(t);
                    break;
                case EH:
                    break;
            }
        }

        /**
         * 左平衡
         */
        public void leftBalance(AVLNode<T> t) {
            AVLNode<T> tl = t.leftChild;

            switch (tl.balance) {
                case LH://1、如果新的结点插入到t的左孩子的左子树中,则直接进行右旋操作即可
//                  t                                      tl
//                 /  \         t右旋操作                  /         \
//                tl   tr       ------------->        tll         t
//               /  \                                /  \        /   \
//              tll  tlr                           lcll  lclr    tlr   tr 
//              /  \
//            lcll   lclr <----插入lcll或lclr
                    rightRotate(t);
                    t.balance = EH;
                    tl.balance = EH;
                    //tll平衡因子不变
                    break;
                case RH://2、如果新的结点插入到t的左孩子的右子树中,则需要进行分情况讨论
                    switch (tl.rightChild.balance) {
                        case RH://情况a:当t的左孩子的右子树根节点的balance = RIGHT_HIGH
                            //          t                       t                           tlr
                            //         /  \                    /  \                        /  \
                            //        tl   6      tl左旋     tlr    6       t右旋             tl    t
                            //       /  \       ------->     /  \        -------->       /    /  \
                            //      3  tlr                 tl    5                      3    5    6
                            //            \                /
                            //             5  <----插入     3
                            t.balance = EH;
                            tl.balance = LH;
                            tl.rightChild.balance = EH;
                            break;
                        case LH://情况b:当t的左孩子的右子树根节点的balance = LEFT_HIGH
                            //          t                       t                          tlr
                            //         /  \                    /  \                        /  \
                            //       tl    6      tl左旋      tlr    6          t右旋        tl    t
                            //      /  \        ------->     /              -------->    / \    \
                            //     3  tlr                   tl                          3   5    6
                            //        /                    / \
                            //       5   <----插入          3   5
                            t.balance = RH;
                            tl.balance = EH;
                            tl.rightChild.balance = EH;
                            break;
                        case EH://情况c:当t的左孩子的右子树根节点的balance = EQUAL_HIGH
                            //          t                       t                          tlr
                            //         /                       /                           /  \
                            //        tl            tl左旋    tlr            t右旋         tl   t
                            //          \          ------->   /            -------->              
                            //         tlr  <----插入        tl                                     
                            t.balance = EH;
                            tl.balance = EH;
                            tl.rightChild.balance = EH;
                            break;
                    }

                    leftRotate(t.leftChild);
                    rightRotate(t);
                    break;
                case EH:
                    break;
            }
        }

        /**
         * 插入元素
         * @param value
         */
        public void insertNode(T value) {
            if (root == null) {//没有元素
                root = new AVLNode<>(value, null, null, null);
            } else {
                Comparable e = value;
                AVLNode<T> head = root;
                AVLNode<T> parent = null;
                int cmp = 0;
                while (head != null) {
                    cmp = e.compareTo(head.value);

                    parent = head;
                    if (cmp == 0) {
                        return;
                    } else if (cmp > 0) {//往右子树偏移
                        head = head.rightChild;
                    } else {
                        head = head.leftChild;
                    }
                }

                if (cmp > 0) {//往右子树插入节点
                    parent.rightChild = new AVLNode<>(value, null, null, parent);
                } else {
                    parent.leftChild = new AVLNode<>(value, null, null, parent);
                }
                
                //平衡因子调整
                while (parent != null) {
                    if (e.compareTo(parent.value) > 0) {//右子树多了深度
                        parent.balance--;
                    } else {
                        parent.balance++;
                    }

                    if(parent.balance == 0){//插入后正好平衡了,不用处理了
                        break;
                    }
                    
                    if (Math.abs(parent.balance) == 2) {//不平衡了
                        //根据平衡因子进行旋转操作
                        calcBalance(parent);
                        break;
                    } else {//继续往上找父亲,调整平衡因子
                        parent = parent.parent;
                    }
                }
            }

            size++;
        }

        /**
         * 处理平衡问题
         * @param parent
         */
        private void calcBalance(AVLNode<T> parent) {
            if (parent.balance == -2) {//右子树多了
                rightBalance(parent);
            } else {
                leftBalance(parent);
            }
        }

        class AVLNode<T> {
            T value;
            AVLNode<T> leftChild;
            AVLNode<T> rightChild;
            AVLNode<T> parent;
            //平衡因子
            int balance = EH;

            public AVLNode(T value, AVLNode<T> leftChild, AVLNode<T> rightChild, AVLNode<T> parent) {
                this.value = value;
                this.leftChild = leftChild;
                this.rightChild = rightChild;
                this.parent = parent;
            }
        }
    }

测试输出:

代码语言:javascript
复制
    public static void main(String args[]) {
        AVLTree<Integer> avlTree = new AVLTree();
        avlTree.insertNode(5);
        avlTree.insertNode(-1);
        avlTree.insertNode(6);
        avlTree.insertNode(4);
        avlTree.insertNode(2);
        avlTree.insertNode(7);
        avlTree.insertNode(0);

        dfs(avlTree);
    }

    private static void dfs(AVLTree<Integer> avlTree) {
        Deque<AVLTree.AVLNode> deque = new LinkedList<>();

        if (avlTree.root != null) {
            deque.offer(avlTree.root);
        }
        while (!deque.isEmpty()) {
            int size = deque.size();
            while (size > 0) {
                AVLTree.AVLNode node = deque.poll();

                System.out.print(node.value + " ");
                if (node.leftChild != null) {
                    deque.offer(node.leftChild);
                }
                if (node.rightChild != null) {
                    deque.offer(node.rightChild);
                }

                size--;
            }

            System.out.println();
        }
    }

结果: 5 2 6 -1 4 7 0

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/8/16 上,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
腾讯云服务器利旧
云服务器(Cloud Virtual Machine,CVM)提供安全可靠的弹性计算服务。 您可以实时扩展或缩减计算资源,适应变化的业务需求,并只需按实际使用的资源计费。使用 CVM 可以极大降低您的软硬件采购成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档