java - 数据结构- 平衡二叉树(AVL树)

平衡二叉树(Balanced Binary Tree), 一般也叫做AVL树

AVL树得名于它的发明者G. M. Adelson-Velsky和E. M. Landis(我才知道。。。)

 

平衡二叉树是一种特殊的二叉排序树,它的任意节点的左子树和右子树的深度之差不超过1.

以此防止左右深度差距过大,导致的查询困难。 (变成平衡二叉树后查询起来就类似于二分查询了)

而其中的难点主要是左旋,右旋,和双旋

首先推荐一个超级好用的数据结构网站= =

https://www.cs.usfca.edu/~galles/visualization/Algorithms.html

可以动态演示各种数据结构

 

再添加完节点后,判断(写在添加方法中,所以会从新节点往上递归对每一个经过的节点进行检查)节点情况

1. 如果一个节点A的左子树高度 - 右子树高度 > 1, A的左子树的右子树高度 > A 的左子树的左子树高度

先对A的左子树左旋,然后对A右旋

2. 如果一个节点A的左子树高度 - 右子树高度 > 1, 且不满足上面那种情况

只对A右旋

3.如果一个节点A的右子树高度 - 左子树高度 > 1, A的右子树的左子树高度 > A的右子树的右子树高度

先对A的右子树右旋,然后对A左旋

4.如果一个节点A的右子树高度 - 左子树高度 > 1, 且不满足上面那种情况

只对A左旋

 

怎么说呢= =|||反正我从来不看别人的代码,看着都头晕。。。真想搞明白还是自己敲一遍练练吧。。。

 

package tree;

public class AVLTree {

    Node root;

    public static void main(String args[]){
        //左旋
//        AVLTree avlTree1 = new AVLTree();
//        avlTree1.addNode(2);
//        avlTree1.addNode(1);
//        avlTree1.addNode(4);
//        avlTree1.addNode(3);
//        avlTree1.addNode(5);
//        avlTree1.addNode(6);
//        avlTree1.show();
//        System.out.println();
//        System.out.println(avlTree1.root.nodeHeight());
//        System.out.println(avlTree1.root.left.nodeHeight());
//        System.out.println(avlTree1.root.right.nodeHeight());

        //右旋
//        AVLTree avlTree2 = new AVLTree();
//        avlTree2.addNode(5);
//        avlTree2.addNode(3);
//        avlTree2.addNode(6);
//        avlTree2.addNode(2);
//        avlTree2.addNode(4);
//        avlTree2.addNode(1);
//        avlTree2.show();
//        System.out.println();
//        System.out.println(avlTree2.root.nodeHeight());
//        System.out.println(avlTree2.root.left.nodeHeight());
//        System.out.println(avlTree2.root.right.nodeHeight());

        //右边高度 - 左边高度 > 1
        //左孩子的右孩子 > 左边的左孩子
        //先左子树左旋转,然后本节点右旋转
//        AVLTree avlTree3 = new AVLTree();
//        avlTree3.addNode(5);
//        avlTree3.addNode(2);
//        avlTree3.addNode(6);
//        avlTree3.addNode(1);
//        avlTree3.addNode(3);
//        avlTree3.addNode(4);
//
//        avlTree3.show();
//        System.out.println();
//        System.out.println("此时根节点为:" + avlTree3.root.value);
//        System.out.println("此时根节点左孩子为:" + avlTree3.root.left.value);
//        System.out.println("此时根节点右孩子为:" + avlTree3.root.right.value);
//
//        System.out.println(avlTree3.root.nodeHeight());
//        System.out.println(avlTree3.root.left.nodeHeight());
//        System.out.println(avlTree3.root.right.nodeHeight());

        //左边高度 - 右边高度 > 1
        //右孩子的左孩子 > 右边的右孩子
        //先右子树右旋转,然后本节点左旋转
        AVLTree avlTree4 = new AVLTree();
        avlTree4.addNode(5);
        avlTree4.addNode(2);
        avlTree4.addNode(8);
        avlTree4.addNode(7);
        avlTree4.addNode(9);
        avlTree4.addNode(6);

        avlTree4.show();
        System.out.println();
        System.out.println("此时根节点为:" + avlTree4.root.value);
        System.out.println("此时根节点左孩子为:" + avlTree4.root.left.value);
        System.out.println("此时根节点右孩子为:" + avlTree4.root.right.value);

        System.out.println(avlTree4.root.nodeHeight());
        System.out.println(avlTree4.root.left.nodeHeight());
        System.out.println(avlTree4.root.right.nodeHeight());
    }

    public AVLTree(){
        this.root = null;
    }


    //添加节点
    public void addNode(int num){
        if(root == null){
            root = new Node(num);
        }
        else{
            this.root.addNode(num);
        }
    }




    //中序遍历 (按顺序从小到大)
    public void show(){
        if(root == null){
            System.out.println("树为空");
        }
        else{
            root.show();
        }
    }

    public class Node{
        public int value;
        public Node left;
        public Node right;

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

        //添加节点
        public void addNode(int num){
            if(num < this.value){
                if(this.left == null) {
                    this.left = new Node(num);
                }
                else{
                    this.left.addNode(num);
                };
            }
            else if(num >= this.value){
                if(this.right == null) {
                    this.right = new Node(num);
                }
                else{
                    this.right.addNode(num);
                };
            }

            //判断是否需要旋转 ,这样递归的时候就可以从底向上依次判断左右高度
            int leftHeight = this.left == null ? 0 : this.left.nodeHeight();
            int rightHeight = this.right == null ? 0 : this.right.nodeHeight();

            if(rightHeight - leftHeight > 1){
                //此时分两种情况
                //如果当前节点右孩子的左孩子高度 > 右孩子的右孩子高度,需要先左旋转,然后右旋转。
                //否则只左旋转
                int rightLeftHeight = this.right.left == null ? 0 : this.right.left.nodeHeight();
                int  rightRightHeight = this.right.right == null ? 0 : this.right.right.nodeHeight();
                if(rightLeftHeight > rightRightHeight ){
                    System.out.println("新加节点" + num + "后需要先对右子树右旋转然后左旋转");
                    this.right.rightRotate();
                    this.leftRotate();
                }
                else{
                    System.out.println("新加节点" + num + "后需要左旋转");
                    this.leftRotate();
                }
            }

            if(leftHeight - rightHeight > 1){
                //此时分两种情况
                //如果当前节点的左子树的右子树高度 > 左子树的左子树高度,需要先右转然后左转
                //否则只需要右转
                int leftLeftHeight = this.left.left == null ? 0 : this.left.left.nodeHeight();
                int leftRightHeight = this.left.right == null ? 0 : this.left.right.nodeHeight();

                if(leftRightHeight > leftLeftHeight){
                    System.out.println("新加节点" + num + "后需要先对左子树左旋转然后右旋转");
                    this.left.leftRotate();
                    this.rightRotate();
                }
                else {
                    System.out.println("新加节点" + num + "后需要右旋转");
                    this.rightRotate();
                }
            }

        }

        //中序遍历
        public void show(){
            if(this.left != null){
                this.left.show();
            }

            System.out.print(this.value + " ");

            if(this.right != null){
                this.right.show();
            }
        }

        //添加节点
        //1.先把节点挂在对应位置,然后判断根节点左右两边的最大深度。
        //所以先咬写判断深度的算法。
        //返回左子树高度
        public int nodeHeight(){

//            if(this.left != null && this.right == null){
//                return this.left.nodeHeight() + 1;
//            }
//            else if(this.left == null && this.right != null){
//                return this.right.nodeHeight() + 1;
//            }
//            else if(this.left == null && this.right == null){
//                return 1;
//            }
//            else {
//                return Math.max(this.left.nodeHeight(), this.right.nodeHeight()) + 1;
//            }
            return Math.max(
                    this.left == null ? 0 : left.nodeHeight(),  //当left == null时, 左边深度为0, 不等于0时, 左边深度为 left.nodeHeight()
                    this.right == null ? 0 : this.right.nodeHeight() //当right == null时, 左边深度为0, 不等于0时, 左边深度为 right.nodeHeight()
            ) + 1;
        }

        //以当前节点为基准左旋 (当前节点左边和右边高度不一样)
        public void leftRotate(){
            //对 A 左旋时
            // 1.创建一个新节点,.把 A 的值赋给 新节点
            // 2.把A的左子树设为新节点的左子树,把新节点放在A的左子树,
            // 3.把A的右子树的左子树设为A的右子树
            // 4. 把A的右子树的值赋给A,把A的右子树的右子树给A作为新的右子树
            //这样避免了找父亲的过程。
//             2                   2                     4                     4
//          1     4        ->   2     4       ->     2      4        ->    2      5
//              3   5         1     3   5          1   3      5          1   3      6
//                    6                   6                      6
            Node newNode = new Node(this.value);
            newNode.left = this.left;
            this.left = newNode;
            newNode.right = this.right.left;
            this.value = this.right.value;
            this.right =this.right.right;
        }

        //以当前节点为基准右旋 (当前节点左边和右边高度不一样)
        public void rightRotate(){
            //对 A 右旋时
            // 1.创建一个新节点,.把 A 的值赋给 新节点
            // 2.把A的右子树设为新节点的右子树,把新节点放在A的右子树,
            // 3.把A的左子树的右子树设为新节点的左子树
            // 4. 把A的左子树的值赋给A,把A的左子树的左子树给A作为新的左子树
            //和左旋正好镜像,把所有的左右互换就可以了-。-
//              5               5                5                3
//          3      6     ->  3     5     ->   3      5     ->   2     5
//        2  4             2  4      6      2      4   6      1     4   6
//      1                1                1
            Node newNode = new Node(this.value);
            newNode.right = this.right;
            this.right = newNode;
            newNode.left = this.left.right;
            this.value = this.left.value;
            this.left = this.left.left;
        }


    }
}

 

 

 

 

posted @ 2019-11-15 14:34  不咬人的兔子  阅读(227)  评论(0编辑  收藏  举报