天天说手撕AVL树?你真的能撕下来吗?(详细解释+代码注释)

目录

前言

一、什么是AVL树?

二、模拟实现AVL树

2.1、定义AVL树

2.2、插入结点功能

2.2.1、先把值为val的结点插入到AVL树中

2.2.2、根据平衡因子来对树进行调整

2.3、AVL树的旋转

2.3.1、右单旋

2.3.2、左单旋

2.3.3、左右双旋

2.3.4、右左双旋

 2.3、验证AVL树

2.4、删除结点

三、性能分析


前言

为什么要引入AVL树?

        我们熟知的二叉搜索树虽然在原来二叉树的基础上已经缩短了查找的效率,但是若数据有序,或接近有序的二叉搜索树将会退化成一个单支树,查找的效率相当于在顺序表中搜索元素,有悖引入二叉搜索树的初衷了;这时候就出现了AVL树,可以很好的接近上述问题;怎么解决?请往下看~

接下来本文将用最简洁明了的话语、最详细的代码、最详细的注释,带你深入以及模拟实现一个AVL树;


一、什么是AVL树?

        当向二叉搜索树中插入新的结点后,如果能保证每一个结点的左右子树高度差(简称平衡因子)的绝对值不超过1(这里是对树的结点通过左右旋转后),那么他就是一颗AVL树。AVL树降低了树的高度,减少了平均搜索长度,提高了搜索效率;

        如果一棵二叉搜索树是高度平衡的,它就是AVL树。如果它有n个结点,其高度可保持在O(log2 n) ,搜索时间复杂度O(log2 n)。

例如下图:(一颗AVL树)


二、模拟实现AVL树

        模拟实现AVL树就需要分别实现两个功能,插入和删除,其中,插入最难,因为涉及到左旋右旋调整树的平衡;这里如果能将插入功能实现,那么删除功能的实现就十分容易了;

2.1、定义AVL树

public class AVLTree {
    static class TreeNode {
        public int val;
        public int bf;//平衡因子
        TreeNode left;//左子树
        TreeNode rigth;//右子树
        TreeNode parent;//父亲结点

        public TreeNode(int val) {
            this.val = val;
        }
    }
    public TreeNode root;//根节点

2.2、插入结点功能

插入结点分两步:

1.先将结点插入到AVL树中(方法和二叉搜索树的插入结点的方式一样);

2.插入以后,根据平衡因子来对树进行调整;

2.2.1、先把值为val的结点插入到AVL树中

思路:

        首先看根结点是否为空,若根节点为空说明这棵树还是一个空树,那么就直接创建一个值为val的根节点;

        若根节点不为空,就用一个cur指针去遍历这个树的结点,并用一个parent指针记录cur的父亲结点,当cur指向的当前结点值大于val,那么就因该向树的左子树走,若小于val,就因该向树的右边走,直到cur为空时,通过比parent与val值,即可确定插入位置;

代码如下:

    public boolean insert(int val) {
        TreeNode node = new TreeNode(val);
        if(root == null) {
            root = node;
            return true;
        }
        TreeNode parent = null;
        TreeNode cur = root;
        while(cur != null) {
            if(cur.val > val) {
                //向左寻找
                parent = cur;
                cur = cur.left;
            } else if(cur.val == val) {
                //相等就说明插入失败
                return false;
            } else {
                //向右寻找
                parent = cur;
                cur = cur.right;
            }
        }
        //cur == null
        if(parent.val > val) {
            parent.left = node;
        } else {
            parent.right = node;
        }
        return true;
    }

2.2.2、根据平衡因子来对树进行调整

平衡因子等于什么?

这里规定为:右树的高度减左树的高度,一旦出现了平衡因子的绝对值大于1,说明这颗树就不平衡了,需要进行旋转使其平衡;

因此首先需要记录平衡因子——新增结点在右树,平衡因子++,新增结点在左树, 平衡因子--;

代码如下:

            if(cur == parent.right) {
                //新增结点在右树
                parent.bf++;
            } else {
                //新增结点在左树
                parent.bf--;
            }

先来看看为什么要调整?

假设树原本是如下样子:

之后,我插入了一个val值为10的结点,那么这颗树就不平衡了(不满足平衡因子了),为什么?看下图:

那么如何旋转呢?接下来先简单了解一下(后面会重点讲)

可以将不平衡的子树进行如下旋转:

 以上只是一种新增结点的方式,但显然,实际情况就右很多种,因此分为以下几种情况:

情况一:平衡因子等于0,这种情况, 树以及平衡;

情况二:平衡因子等于1或-1,这种情况,树不一定平衡,还需要向上去看他的父亲结点是否平衡;

情况三:平衡因子等于2或-2, 这种情况,树一定不平衡,根据实际情况,降低高度;

写成代码就是以下情况:(还没有写如何处理)

            if(parent.bf == 0) {
                //这里说明以及平衡了,不需要在调整
                break;
            } else if(parent.bf == 1 || parent.bf == -1) {
                //再往上查看结点,有可能不平衡
                cur = parent;
                parent = cur.parent;
            } else {
                //一定不平衡
                if(parent.bf == 2) {//右树高,需要降低右树的高度
                    if(cur.bf == 1) {

                    } else {//cur.bf == -1

                    }
                } else {//parent.bf == -2  左树高,需要降低左树的高度
                    if(cur.bf == -1) {

                    } else {//cur.bf == 1

                    }
                }
            }

针对以上一些不平衡的情况,我们因该如何处理呢?旋转!(重难点!)

2.3、AVL树的旋转

2.3.1、右单旋

什么时候需要右单旋呢?

新结点插入较高左子树的左侧,原因如下图

        分析:若是插入 较高左子树的左侧 那么代码根据上图的平衡因子的变化,最终代码会来到下图这个位置

 那么现在问题就在于,如何进行右单旋,使其变成一颗平衡的树?

 如下这棵树:

 变换后可以得到:

这样左旋转就完了:

parent : 结点60;

subL : 结点30;

subLR:结点40;

代码如下:

private void rotateRight(TreeNode parent) {
        TreeNode subL = parent.left;
        TreeNode subLR = subL.right;
        parent.left = subLR;

        subL.right = parent;
        
        subLR.parent = parent;

        parent.parent = subL;
    }

怎么可能这么容易哦~

因为60这个结点不一定是根节点,如下:

所以,代码还需要分为以下情况:(60有父亲结点和没有父亲结点)

    //右单旋
    private void rotateRight(TreeNode parent) {
        TreeNode subL = parent.left;
        TreeNode subLR = subL.right;
        parent.left = subLR;

        subL.right = parent;

        subLR.parent = parent;

        //必须先记录parent的父亲
        TreeNode pParent = parent.parent;

        parent.parent = subL;
        //检查parent是否是根节点
        if(parent == root) {
            root = subL;
            root = null;
        } else {
            //不是根节点,判断parent是左子树还是右子树
            if(pParent.left == parent) {
                pParent.left = subL;
            } else {
                pParent.right = subL;
            }
            subL.parent = pParent;
        }
    }

 不光转化完了,还需要调节他的平衡因子,如下:

    //右单旋
    private void rotateRight(TreeNode parent) {
        TreeNode subL = parent.left;
        TreeNode subLR = subL.right;
        parent.left = subLR;

        subL.right = parent;

        subLR.parent = parent;

        //必须先记录parent的父亲
        TreeNode pParent = parent.parent;

        parent.parent = subL;
        //检查parent是否是根节点
        if(parent == root) {
            root = subL;
            root = null;
        } else {
            //不是根节点,判断parent是左子树还是右子树
            if(pParent.left == parent) {
                pParent.left = subL;
            } else {
                pParent.right = subL;
            }
            subL.parent = pParent;
        }
        subL.bf = 0;
        parent.bf = 0;
    }

 写到这里,以上代码还有一点小问题,就是subLR有可能是空,如下:

怎么解决呢?实际上如果subRL是null,那么不执行 subRL.parent = parent这行代码即可,那么此时,需要加上一个判断,如下:

    //右单旋
    private void rotateRight(TreeNode parent) {
        TreeNode subL = parent.left;
        TreeNode subLR = subL.right;
        parent.left = subLR;
        subL.right = parent;
        if(subLR != null) {
            subLR.parent = parent;
        }
        //必须先记录parent的父亲
        TreeNode pParent = parent.parent;
        parent.parent = subL;
        //检查parent是否是根节点
        if(parent == root) {
            root = subL;
            root = null;
        } else {
            //不是根节点,判断parent是左子树还是右子树
            if(pParent.left == parent) {
                pParent.left = subL;
            } else {
                pParent.right = subL;
            }
            subL.parent = pParent;
        }
        subL.bf = 0;
        parent.bf = 0;
    

这样右单旋就完成了~

2.3.2、左单旋

什么时候需要左单旋呢?

新结点插入较高右子树的右侧,原因如下图

代码上,和右单旋是差不多的,可以参照着右单旋来写:

    //左单旋
    private void rotateLeft(TreeNode parent) {
        TreeNode subR = parent.right;
        TreeNode subRL = subR.left;
        parent.right = subRL;

        subR.left = parent;

        if(subRL != null) {
            subRL.parent = parent;
        }
        //必须先记录parent的父亲
        TreeNode pParent = parent.parent;
        parent.parent = subR;
        //检查parent是否为根节点
        if(parent == root) {
            root = subR;
            root.parent = null;
        } else {
            //不是根节点需要判断parent是左子树还是右子树
            if(pParent.left == parent) {
                pParent.left = subR;
            } else {
                pParent.right = subR;
            }
            subR.parent = pParent;
        }
        subR.bf = 0;
        parent.bf = 0;
    }

由此通过左单旋,和右单旋也可以看出循环条件为parent != null,原因:一直在搜索不平衡因子是一个向上搜索的过程,直到parent为null,说明已经平衡因子已经调节到了到根节点(修改完了),因此代码就可以修改:

    public boolean insert(int val) {
        TreeNode node = new TreeNode(val);
        if(root == null) {
            root = node;
            return true;
        }
        TreeNode parent = null;
        TreeNode cur = root;
        while(cur != null) {
            if(cur.val > val) {
                //向左寻找
                parent = cur;
                cur = cur.left;
            } else if(cur.val == val) {
                //相等就说明插入失败
                return false;
            } else {
                //向右寻找
                parent = cur;
                cur = cur.right;
            }
        }
        //cur == null
        if(parent.val > val) {
            parent.left = node;
        } else {
            parent.right = node;
        }

        cur = node;
        //调整因子
        while(parent != null) {
            //新增结点在右树,平衡因子++,新增结点在左树, 平衡因子--
            if(cur == parent.right) {
                //新增结点在右树
                parent.bf++;
            } else {
                //新增结点在左树
                parent.bf--;
            }

            //检查平衡因子大小
            if(parent.bf == 0) {
                //这里说明以及平衡了,不需要在调整
                break;
            } else if(parent.bf == 1 || parent.bf == -1) {
                //再往上查看结点,有可能不平衡
                cur = parent;
                parent = cur.parent;
            } else {
                //一定不平衡
                if(parent.bf == 2) {//右树高,需要降低右树的高度
                    if(cur.bf == 1) {
                        //右旋
                        rotateLeft(parent);
                    } else {//cur.bf == -1

                    }
                } else {//parent.bf == -2  左树高,需要降低左树的高度
                    if(cur.bf == -1) {
                        //右旋
                        rotateRight(parent);
                    } else {//cur.bf == 1

                    }
                }
            }
        }
    }

这样左单旋就完成了~

2.3.3、左右双旋

什么时候需要右单旋呢?

新结点插入较高左子树的右侧(先左单旋再右单旋),原因如下图:

 像这样的一棵树如果只是进行简单的左旋或右旋,相当于旋转了个寂寞,如下:

那怎么办呢?不妨这样做~

第一步:可以先对30这颗子树进行左旋,如下:

第二步:再将90这个parent结点进行右旋,如下:

那么此时,他就是一个高度平衡的AVL树了~

左右双旋,实际上就是左单旋 + 右单旋,并且这部分代码之前就写好了,所以直接拿来用就可以,值得注意的一点是,要旋转的结点,这个可以自己画画图就明白了;

代码如下:

    //左右双旋
    private void rortateLR(TreeNode parent) {
        //选左旋后右旋
        rotateLeft(parent.left);//注意旋转的是parent的左子树
        rotateRight(parent);
        
    }

 这样就完了吗?

当然没有~这里还有平衡因子没有修改呢!

 这里平衡因子分两种情况,接下来,我将用一张图告诉你该如何解决!如下:

因此,代码如下:

    //左右双旋
    private void rortateLR(TreeNode parent) {
        TreeNode subL = parent.left;
        TreeNode subLR = subL.right;
        int bf = subLR.bf;
        //选左旋后右旋
        rotateLeft(parent.left);//注意旋转的是parent的左子树
        rotateRight(parent);
        if(bf == -1) {
            subL.bf = 0;
            subLR.bf = 0;
            parent.bf = 1;
        } else if(bf == 1){
            subL.bf = -1;
            subLR.bf = 0;
            parent.bf= 0;
        } //bf == 0,这里本身就是平衡的,就不用修改
    }

这样左右双旋就完成辽~

2.3.4、右左双旋

什么时候需要右左双旋呢?

新结点插入较高右子树的左侧,原因如下图

这里代码和左右双旋差不多,分析思路也一样,所以这里就不展开啦,代码如下:


    //右左双旋
    private void rottateRL(TreeNode parent) {
        TreeNode subR = parent.right;
        TreeNode subRL = subR.left;
        int bf = subRL.bf;
        rotateRight(parent.right);
        rotateLeft(parent);
        if(bf == 1) {
            parent.bf = -1;
            subR.bf = 0;
            subRL.bf = 0;
        } else if(bf == -1) {
            subRL.bf = 0;
            parent.bf = 0;
            subR.bf = 1;
        }
    }

由以上的各种旋转,那么之前遗留的insert函数中的代码也就完整啦,如下:

    public boolean insert(int val) {
        TreeNode node = new TreeNode(val);
        if(root == null) {
            root = node;
            return true;
        }
        TreeNode parent = null;
        TreeNode cur = root;
        while(cur != null) {
            if(cur.val > val) {
                //向左寻找
                parent = cur;
                cur = cur.left;
            } else if(cur.val == val) {
                //相等就说明插入失败
                return false;
            } else {
                //向右寻找
                parent = cur;
                cur = cur.right;
            }
        }
        //cur == null
        if(parent.val > val) {
            parent.left = node;
        } else {
            parent.right = node;
        }
        node.parent = parent;/
        cur = node;
        //调整因子
        while(parent != null) {
            //新增结点在右树,平衡因子++,新增结点在左树, 平衡因子--
            if(cur == parent.right) {
                //新增结点在右树
                parent.bf++;
            } else {
                //新增结点在左树
                parent.bf--;
            }

            //检查平衡因子大小
            if(parent.bf == 0) {
                //这里说明以及平衡了,不需要在调整
                break;
            } else if(parent.bf == 1 || parent.bf == -1) {
                //再往上查看结点,有可能不平衡
                cur = parent;
                parent = cur.parent;
            } else {
                //一定不平衡
                if(parent.bf == 2) {//右树高,需要降低右树的高度
                    if(cur.bf == 1) {
                        //左旋
                        rotateLeft(parent);
                    } else {//cur.bf == -1
                        //右左双旋
                        rottateRL(parent);
                    }
                } else {//parent.bf == -2  左树高,需要降低左树的高度
                    if(cur.bf == -1) {
                        //右旋
                        rotateRight(parent);
                    } else {//cur.bf == 1
                        //左右双旋
                        rortateLR(parent);
                    }
                }
                //走完以上代码即哦平衡
                break;
            }
        }
        return true;
    }

 2.3、验证AVL树

验证一棵树是否为AVL树只需要验证他的以下两个性质即可~

1)、验证其为二叉搜索树

  • 如果中序遍历可得到一个有序的序列,就说明为二叉搜索树;

2)、验证其为平衡树

  • 每个节点子树高度差的绝对值不超过1(注意节点中如果没有平衡因子)
  • 节点的平衡因子是否计算正确

代码如下:

    //验证平衡
    public boolean isBalanced(TreeNode root) {
        if(root == null) {
            return true;
        }
        int leftH = heigh(root.left);
        int rightH = heigh(root.right);
        if(rightH - leftH != root.bf) {
            System.out.println("结点:" + root.val + "的平衡因子异常");
            return false;
        }
        return Math.abs(leftH - rightH) <= 1 &&
                isBalanced(root.left) &&
                isBalanced(root.right);
    }
    //求树的高度
    private int heigh(TreeNode root) {
        if(root == null) {
            return 0;
        }
        int leftH = heigh(root.left);
        int rightH = heigh(root.right);
        return leftH > rightH ? leftH + 1 : rightH + 1;
    }
    //验证顺序
    public void dfs(TreeNode root) {
        if(root == null) {
            return;
        }
        dfs(root.left);
        System.out.print("root = " + root.val + "; ");
        dfs(root.right);
    

2.4、删除结点

删除结点这里不需要掌握,感兴趣的可以自己实现一下;

如果要删除结点,实际上和二叉搜索树结点的删除思路相似,只不过需要进行对平衡因子的更新,并且若出现了不平衡需要进行左旋或者右旋;

代码如下:

    /**
     * 删除操作
     */
    public AVLNode<T> deleteNode(AVLNode T,T value){
        if(T==null){
            return null;
        }else{
            //往左走
            if((Integer)value<(Integer) T.data){
                T.lchild = deleteNode(T.lchild,value);  //函数返回新的根节点(所以要重新建立孩子与双亲间的联系)
                if(getHeight(T.rchild)-getHeight(T.lchild)>=2){ //删掉左子树的孩子结点,肯定是右边可能会高过左边的情况
                    if(getHeight(T.rchild.lchild)>getHeight(T.rchild.rchild)){  //左子树的 右子树比左子树的左子树要高
                        //RL旋转
                        T = doubleRotateWithRight(T);    //记住,因为旋转后,根节点会发生变化,一定要重新接收根结点
                    }else{
                        //RR
                        T = singleRotateWithLeft(T);
                    }
                }
                //往右走
            }else if((Integer)value>(Integer)T.data){
                T.rchild = deleteNode(T.rchild,value);
                if(getHeight(T.lchild)-getHeight(T.rchild)>=2){   //删掉右子树的孩子结点,肯定是左边可能会高过右边的情况
                    if(getHeight(T.lchild.rchild)>getHeight(T.lchild.lchild)){
                        //LR旋转
                        T = doubleRotateWithLeft(T);
                    }else{
                        //LL
                        T = singleRotateWithRight(T);
                    }
                }
            }else{
                //找到了要删除的结点
 
                //1. 没有左右孩子,删除的是叶子节点  (不用判断是否需要修复--旋转)
                if(T.lchild==null&&T.rchild==null){
                    T = null;
                    //2. 删除的结点只有左孩子或右孩子
                }else {
                    if(T.lchild!=null){
                        T = T.lchild;
                    }else if(T.rchild!=null){
                        T = T.rchild;
                    }else{
                        //3. 删除的结点左右孩子都有
                        T.data = find_min_value(T.rchild);  //找到最小节点,替换
                        T.rchild = deleteNode(T.rchild,(T)T.data);  //删除替换的最小的那个结点
 
                        //判断旋转
                        if(getHeight(T.lchild)-getHeight(T.rchild)>=2){
                            if(getHeight(T.lchild.rchild)-getHeight(T.lchild.lchild)>=2){
                                //LR
                                T = doubleRotateWithLeft(T);
                            }else{
                                //LL
                                T = singleRotateWithRight(T);
                            }
                        }
                    }
                }
            }
        }
        if(T!=null){
            //重新计算高度
            T.height = Max(getHeight(T.lchild),getHeight(T.rchild))+1;
        }
 
        //返回新的根节点
        return T;
 
    }
 
    /**
     * 找到最小的结点值
     */
    public T find_min_value(AVLNode T){
 
        if(T.lchild==null){
            return (T) T.data;
        }else{
            return find_min_value(T.lchild);
        }
    }
 
    /**
     * 用于比较两棵子树高度,比较哪边高 ,用于节点高度 = Max(T.lchild.height,T.rchild.height)+1
     */
    public int Max(int lHeight,int rHeight){
        if(lHeight>=rHeight){
            return lHeight;
        }else{
            return rHeight;
        }
    }
 
    /**
     * 获取结点高度,因为可能计算高度的时候,左右孩子结点很可能为空,如果不用这个方法判断的话,会导致nullPointerException
     */
    public int getHeight(AVLNode T){
        if(T==null){
            return -1;
        }else{
            return T.height;
        }
    }


三、性能分析

        AVL树是一棵绝对平衡的二叉搜索树,其要求每个节点的左右子树高度差的绝对值都不超过1,这样可以保证查询时高效的时间复杂度,即 log2(N);

        AVL树不适合大量的插入和删除,因为要不断维持本身AVL树平衡的性质,但是他的适合查找数据,速度相当快;也就是说他的平衡说通过大量旋转换来的~

总结:

        如果需要 一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变),可以考虑AVL树,但一个结构经常修 改,就不太适合。


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
【优质项目推荐】 1、项目代码均经过严格本地测试,运行OK,确保功能稳定后才上传平台。可放心下载并立即投入使用,若遇到任何使用问题,随时欢迎私信反馈与沟通,博主会第一时间回复。 2、项目适用于计算机相关专业(如计科、信息安全、数据科学、人工智能、通信、物联网、自动化、电子信息等)的在校学生、专业教师,或企业员工,小白入门等都适用。 3、该项目不仅具有很高的学习借鉴价值,对于初学者来说,也是入门进阶的绝佳选择;当然也可以直接用于 毕设、课设、期末大作业或项目初期立项演示等。 3、开放创新:如果您有一定基础,且热爱探索钻研,可以在此代码基础上二次开发,进行修改、扩展,创造出属于自己的独特应用。 欢迎下载使用优质资源!欢迎借鉴使用,并欢迎学习交流,共同探索编程的无穷魅力! 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。
AVL树是一种自平衡的二叉查找树,它的命名来自于发明者Adelson-Velsky和Landis的名字。AVL树要求任何节点的左右子树高度差不超过1,通过旋转操作来维持平衡,从而保证了AVL树的最长路径不会超过最短路径的两倍,是一种高度平衡的二叉查找树。 与AVL树不同的是,红黑树是另一种自平衡的二叉查找树,它通过对节点进行颜色标记,通过旋转和变色操作来保证树的平衡。红黑树的平衡性不如AVL树强,但是插入、删除和查找操作的平均时间复杂度仍然保持在O(logn)级别。 AVL树和红黑树的区别主要体现在以下几个方面: 1. 平衡性:AVL树要求任何节点的左右子树高度差不超过1,因此它是一种高度平衡的二叉查找树;而红黑树只要求任何节点的左右子树的黑色节点数目相等,因此它是一种近似平衡的二叉查找树。 2. 插入和删除操作的效率:由于AVL树的平衡要求比较严格,因此在插入和删除节点时需要进行更多的旋转操作,导致效率相对较低;而红黑树的平衡要求相对宽松,因此在插入和删除节点时需要进行的旋转操作相对较少,效率较高。 3. 查询操作的效率:由于AVL树是一种高度平衡的二叉查找树,因此在进行查询操作时,树的高度较低,效率较高;而红黑树的平衡性较AVL树稍差,因此在进行查询操作时,树的高度相对较高,效率较低。 综上所述,AVL树和红黑树各有优缺点,需要根据具体的应用场景选择合适的数据结构。如果需要在插入、删除和查询操作中都要求较高的效率,可以考虑使用红黑树;如果需要保证树的高度平衡,可以使用AVL树

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陈亦康

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值