美文网首页
数据结构之AVL树

数据结构之AVL树

作者: QiShare | 来源:发表于2021-11-04 14:40 被阅读0次

    AVL树定义

    AVL树任意节点的两棵子树的高度差绝对值最大为1(和红黑树相比简洁了很多)

    与红黑树对比

    相同点

    • 都是平衡二叉树

    不同点

    • 平衡的定义不同,AVL树的平衡的因子是左右子树的高度差,红黑树的平衡因子是到叶子节点的黑色节点的个数
    • 效率不同,AVL树查找效率高于红黑树,插入和删除效率低于红黑树

    AVL树的节点信息

        private int value;//节点Key值
        private Node left;//左子树
        private Node right;//右子树
        private Node parent;//父节点
        private int leftHeight;//左子树的高度
        private int rightHeight;//右子树高度
    

    旋转操作

    左旋和右旋(旋转和红黑树一样,这一节可以略过),旋转操作是很多树和堆的基础操作,很重要。

    image.png

    左旋转 得到右边的图,右旋转得到左边的图,左旋和右旋是相反的两个操作,旋转不会改变二叉查找树的性质。

    /**
         * 左旋转代码实现
         *
         * @param node
         */
    
        public void leftRoate(Node node){
            if(node == null){
                return;
            }
            Node pNode =node.getParent();
            if(pNode == null){
                return;
            }
            Node lChild =node.getLeft();
            node.setParent(pNode.getParent());
            if(pNode.getParent()!=null) {
                if (pNode.getParent().getLeft() == pNode) {
                    pNode.getParent().setLeft(node);
    
                }else {
                    pNode.getParent().setRight(node);
                }
            }
            pNode.setParent(node);
            node.setLeft(pNode);
            pNode.setRight(lChild);
            if(lChild!=null)
            lChild.setParent(pNode);
    
        }
        /**
         * 右旋转代码实现
         *
         * @param node
         */
        public void rightRoate(Node node){
            if(node ==null){
                return;
            }
            Node pNode =node.getParent();
            if(pNode ==null){
                return;
            }
            Node rChild =node.getRight();
            node.setParent(pNode.getParent());
            if(pNode.getParent()!=null) {
                if (pNode.getParent().getLeft() == pNode) {
                    pNode.getParent().setLeft(node);
    
                }else {
                    pNode.getParent().setRight(node);
                }
            }
            pNode.setParent(node);
            node.setRight(pNode);
            pNode.setLeft(rChild);
            if(rChild!=null)
            rChild.setParent(pNode);
    
        }
    

    AVL树的平衡

    根据AVL树的定义可知,只有左右子树高度差大于1,才会引起不平衡。插入和删除有可能会导致树的高度发生变化,从而破坏平衡状态。当节点的左右子树高度差不满足AVL树的定义,就要进行平衡操作。下面这张图描述的是四种需要调整的不平衡状态,这四种不平衡状态最终都要调整为平衡状态

    image.png

    <center>三角形表示是树,树有可能是空也有可能包含多个节点,圆圈表示单个节点</center>

    如上图所示

    • 左上角图:A的左子树的高度比A的右子树高度大2,同时B的左子树高度比右子树大1,称之为LL型。解决方案:以节点B为轴心右旋
    • 右上角图:右上角图和左上角图是对称的,C节点的右子树高度比左子树大2,同时B节点的右子树比左子树大1,称之为RR型。解决方案:以B为轴心左旋
    • 左下角图:C的右子树的高度比左子树大2,,A的左子树的高度比右子树大1,称之为RL型。解决方案:以B为轴心右旋转得到RR型,然后按RR型进行处理
    • 右下角图:A的左子树比比右子树大2,C的右子树高度比左子树大2,称之为LR型。解决方案:以B为轴心左旋转得到LL型,然后按LL型进行后续处理

    上述的LL,RR,RL,LR这四种就是插入和删除节点后可能遇到的所有不平衡的情况。如果上面那张图内感觉容比较多的话,可以简化成下面这张图(也就是abcd这四棵树为空),这样看起来会比较清晰,如下图

    image.png

    代码实现

    主要针对LL型、LR型、RR型、RL型的不平衡状况通过旋转进行平衡操作,

      //节点更新树高度,left.getMaxHeight()获取的是当前节点左右子树的最大高度
    
       public  void updateHeight(){
            if(left!=null){
                leftHeight = left.getMaxHeight()+1;
            }else {
                leftHeight =0;
            }
            if(right!=null){
                rightHeight =right.getMaxHeight()+1;
            }else {
                rightHeight =0;
            }
    
        }
    //进行平衡调整
    //节点旋转后,有的子节点会变成父节点,父节点会变成子节点,一定要搞清楚,旋转后谁是父节点,谁是子节点。还有旋转后,
    //注意节点左右孩子高度发生变化,要及时更新
      @Override
        public void blanceTree(Node node) {
            if(node ==null){
                return;
            }
            node.updateHeight();
            Node leftChild = node.getLeft();
            Node rightChild = node.getRight();
            
            if(node.getLeftHeight() -node.getRightHeight()>1){//左子树高于右子树
                if(leftChild.getLeftHeight()>leftChild.getRightHeight()){//LL类型
                    rightRoate(leftChild);
                     node.updateHeight();
                     leftChild.updateHeight();
                     blanceTree(leftChild);
                }else {//LR类型
                    leftRoate(leftChild.getRight());
                    leftChild.updateHeight();
    
                    rightRoate(leftChild.getParent());
                    node.updateHeight();
                    leftChild.getParent().updateHeight();
    
                    blanceTree(leftChild.getParent());
    
                }
    
            }else if (node.getLeftHeight() -node.getRightHeight()<-1){//右子树高于左子树
                if(rightChild.getRightHeight()>rightChild.getLeftHeight()) {//RR型
                    leftRoate(rightChild);
                    node.updateHeight();
                    rightChild.updateHeight();
                    blanceTree(rightChild);
                }else {//RL型
                    rightRoate(rightChild.getLeft());
                    rightChild.updateHeight();
                    rightChild.getParent().updateHeight();
                    leftRoate(rightChild.getParent());
                    node.updateHeight();
                    rightChild.getParent().updateHeight();
                    blanceTree(rightChild.getParent());
                }
    
            }else {
                blanceTree(node.getParent());
            }
    
    
        }
    

    平衡操作是本篇文章核心内容,也是AVL树删除和插入节点的灵魂步骤,这一节是重点。

    节点插入

    AVL树也是二叉树,其插入和二叉树的插入一样,插入之后为了保持AVL树特性,要做好节点高度更新以及平衡处理

     public void insert(int key) {
            Node node = createNode(key);
            if (tree == null) {//如果树为空
                tree = node;
                return;
            }
            Node insertNode = insertNode(node, getRoot());
            if(insertNode ==null){
                return;
            }
            System.out.println("插入节点:"+insertNode.getValue());
            try{
                updateTreeheight(insertNode);
    
               blanceTree(insertNode);
            }catch (Exception e){
                e.printStackTrace();
            }
    
    
    
        }
     //将节点插入树中
        public Node insertNode(Node node,Node root){
    
            if(root.getValue()>node.getValue()){//插入左子树
                if(root.getLeft()!=null){
                    return insertNode(node,root.getLeft());
                }else {
                    root.setLeft(node);
                    node.setParent(root);
                    return  node;
    
                }
            }else  if (root.getValue() <node.getValue()){//插入右子树
                if(root.getRight() !=null){
                    return insertNode(node,root.getRight());
                }else {
                    root.setRight(node);
                    node.setParent(root);
                    return node;
                }
            }else{//这个节点废弃掉
                return  null;
            }
        }
    
    

    总结

    AVL树和红黑树都是平衡二叉树,难易程度上,个人感觉,AVL树相对简单些;在效率上,各有侧重,AVL树主要败在维护高度平衡上;在使用范围上,红黑树使用更广泛。在它们进行抉择时,在大多数情况下红黑树是优秀的。

    相关文章

      网友评论

          本文标题:数据结构之AVL树

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