二叉排序树 -- AVL树 红黑树

手撕 – AVL树、红黑树

在这里插入图片描述

个人主页:顾漂亮

文章专栏:Java数据结构

1.AVL树

1.1AVL树的概念

二叉搜索树虽然可以缩短查找的效率,但是如果**数据有序或者接近有序将退化为单支树,查找元素相当于在顺序表中搜索元素,**效率低下。因此:当向二叉搜索树中插入新节点后,如果能保证每一个节点的左右子树高度只差的绝对值不超过1,即通过旋转降低树的高度,从而减少平均搜索长度

1.2AVL树的性质

  • 左右子树都是AVL树
  • 左右子树高度差(简称平衡因子)的绝对值不超过1
  • AVL是一颗高度平衡的二叉平衡树,如果其有n个节点,其高度可以保持在logn,搜索时间复杂度为O(logn)

1.3AVL树的实现 – Java代码

public class AVLTree {
    //定义节点
    static class TreeNode{
        public int val;
        public TreeNode left;
        public TreeNode right;
        public int bf;//平衡因子
        public TreeNode parent;//父节点

        public TreeNode(int val) {
            this.val = val;
        }
    }

    public TreeNode root;
    public boolean insert(int val){
        //先按照二叉搜索树的方式插入新节点
        TreeNode node = new TreeNode(val);
        if(root == null){
            root = node;
            return true;
        }
        TreeNode cur = root;
        TreeNode parent = null;
        while(cur != null){
            if(cur.val > val){
                parent = cur;
                cur = cur.left;
            } else if (cur.val == val) {
                return false;//AVL树中不存在数值相同的节点,如果找到相等节点直接返回false
            }else{
                //cur.val < val
                parent = cur;
                cur = cur.right;
            }
        }
        if(parent.val > val){
            parent.left = node;
            cur = parent.left;
        }else{
            parent.right = node;
            cur = parent.right;
        }
        //不要忘了更新新节点的父亲节点
        node.parent = parent;

        //调整节点的平衡因子
        while(parent != null){
            //更新双亲节点的平衡因子
            if(cur == parent.right){
                parent.bf++;
            } else if (cur == parent.left) {
                parent.bf--;
            }
            //满足AVL树的性质,插入成功
            if(parent.bf == 0){
                break;
            } else if (parent.bf == -1 || parent.bf == 1) {
                //继续向上查找
                cur = parent;
                parent = parent.parent;
            } else  {
                //平衡因子为2
                if(parent.bf == 2){
                    if(cur.bf == -1){
                        rotateRL(parent);
                    }else{
                        //左旋
                        rotateL(parent);
                    }
                }else if (parent.bf == -2){
                    //平衡因子为-2
                    if(cur.bf == -1){
                        //右旋
                        rotateR(parent);
                    }else{
                        rotateLR(parent);
                    }
                }
                break;
            }
        }
        return true;
    }

    private void rotateRL(TreeNode parent) {
        //记录
        TreeNode subR = parent.right;
        TreeNode subRL = subR.left;
        int bf = subRL.bf;
        rotateR(parent.right);
        rotateL(parent);
        if(bf == -1){
            subRL.bf = 0;
            subR.bf = 1;
            parent.bf = 0;
        }else if(bf == 1){
            subR.bf = 0;
            parent.bf = -1;
            subRL.bf = 0;
        } else if (bf == 0) {
            subR.bf = 0;
            parent.bf = 0;
            subRL.bf = 0;
        }
    }

    private void rotateLR(TreeNode parent) {
        //记录
        TreeNode subL = parent.left;
        TreeNode subLR = subL.right;
        int bf = subLR.bf;
        rotateL(parent.left);
        rotateR(parent);
        if(bf == -1){
            subLR.bf = 0;
            subL.bf = 0;
            parent.bf = 1;
        }else if(bf == 1){
            subL.bf = -1;
            parent.bf = 0;
            subLR.bf = 0;
        } else if (bf == 0) {
            subLR.bf = 0;
            subL.bf = 0;
            parent.bf = 0;
        }
    }

    private void rotateR(TreeNode parent) {
        TreeNode subL = parent.left;
        TreeNode subLR = subL.right;
        parent.left = subLR;
        if(subLR != null){
            subLR.parent = parent;
        }
        subL.right = parent;
        TreeNode pParent = parent.parent;
        parent.parent = subL;
        if(root == parent){
            root = subL;
            root.parent = null;
        }else{
            if(parent == pParent.right){
                pParent.right = subL;
            }else{
                pParent.left = subL;
            }
            subL.parent = pParent;
        }
        parent.bf = subL.bf = 0;
    }

    private void rotateL(TreeNode parent) {
        TreeNode subR = parent.right;
        TreeNode subRL = subR.left;
        parent.right = subRL;
        if(subRL != null){
            subRL.parent = parent;
        }
        subR.left = parent;
        TreeNode pParent = parent.parent;
        parent.parent = subR;
        if(parent == root){
            root = subR;
            root.parent = null;
        }else{
            if(parent == pParent.right){
                pParent.right = subR;
            }else{
                pParent.left = subR;
            }
            subR.parent = pParent;
        }
        parent.bf = subR.bf = 0;
    }

    //AVL树的验证
    public void inOrder(TreeNode root){
        if(root == null){
            return;
        }
        inOrder(root.left);
        System.out.print(root.val + " ");
        inOrder(root.right);
    }
    //求树的高度
    public int height(TreeNode root){
        if(root == null){
            return 0;
        }
        int left = height(root.left);
        int right = height(root.right);
        return left > right ? left + 1 : right +1;
    }
    //判断树的高度差
    public boolean isBalance(TreeNode root){
        if(root == null){
            return true;
        }
        int leftH = height(root.left);
        int rightH = height(root.right);

        //检查平衡因子是否有问题
        if(rightH - leftH != root.bf){
            System.out.println(root.val + "平衡因子异常");
            return false;
        }
        return Math.abs(leftH-rightH)<=1 && isBalance(root.left) && isBalance(root.right);
    }
}

注解:

当前节点的平衡因子:当前节点右子树高度 - 左子树高度

1.4AVL树的性能分析

  1. AVL可以保证高效的查找时间复杂度,为O(logN)
  2. 如果需要对于AVL树的结构做一些修改的操作,性能非常低下,比如:插入时要维护其绝对平衡,旋转次数比较多,更差的是在删除时,有可能一直要让旋转持续到根的位置。
  3. 适用场景:适用于需要一种查询高效且有序的数据结构,而且数据的个数是静态的,可以考虑AVL树,但是一个结构需要经常修改,就不太合适

2.红黑树

2.1概念

红黑树,是一种二叉搜索树,但每个节点上增加一个存储位表示节点的颜色,可以是RED或者BLACK。通过对任何一条从根到叶子的路径上各个节点的着色方式的限制,红黑树确保没有一条路径会比其他路径长出两倍,因而是接近平衡的

2.2红黑树的性质

  1. 最长路径最多是最短路径的二倍
  2. 每一个节点不是红色就是黑色
  3. 根节点是黑色的
  4. 如果一个节点是红色的,则它的两个孩子节点是黑色的 没有两个连续的红色节点
  5. 对于每一个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点
  6. 每一个叶子节点都是黑色的(此处叶子节点指的是空节点)

在这里插入图片描述

注意:一般情况下,一个正常的红黑树不会出现上述情况,可以用来验证性质1

2.3红黑树的实现

//定义一个枚举
public enum Color {
    RED,BLACK
}
public class RBTree {
    //红黑树节点
    static class RBTreeNode{
        public int val;
        public RBTreeNode left;
        public RBTreeNode right;
        public RBTreeNode parent;
        Color color;//节点默认为红色,若为黑色,容易产生不平衡因素--需要新增节点
        public RBTreeNode(int val) {
            this.val = val;
            this.color = Color.RED;
        }
    }
    public RBTreeNode root;
    public boolean insert(int val){
        RBTreeNode node = new RBTreeNode(val);
        if(root == null){
            root = node;
            root.color = Color.BLACK;//注意将节点置为黑色,根节点
            return true;
        }
        RBTreeNode cur = root;
        RBTreeNode parent = null;
        while(cur != null){
            if(cur.val > val){
                parent = cur;
                cur = cur.left;
            }else if(cur.val < val){
                parent = cur;
                cur = cur.right;
            }else{
                return false;//红黑树中不存在相同节点
            }
        }
        if(parent.val > val){
            parent.left = node;
            node.parent = parent;//不要忘记确定parent的指向
        } else if (parent.val < val) {
            parent.right = node;
            node.parent = parent;
        }
        cur = node;//cur指向新插入的节点
        //插入节点默认为红色,主要是看插入节点的父节点是否为红色
        while(parent != null && parent.color == Color.RED){
            RBTreeNode grandFather = parent.parent;//若满足parent为红色,则节点一定存在
            if(parent == grandFather.left){
                RBTreeNode uncle = grandFather.right;
                //情况1
                if(uncle!= null && uncle.color == Color.RED){
                    parent.color = Color.BLACK;
                    uncle.color = Color.BLACK;
                    grandFather.color = Color.RED;
                    //判断parent是否为空,即grandparent是否就等于root
                    if(grandFather == root){
                        grandFather.color = Color.BLACK;//将根节点置为空
                        break;
                    }
                    //继续向上修改
                    cur = grandFather;
                    parent = cur.parent;

                }else{
                    //情况3
                    if(parent.right == cur){
                        rotateL(parent);
                        //交换
                        RBTreeNode tmp = cur;
                        cur = parent;
                        parent = tmp;
                    }
                    //情况2
                    rotateR(grandFather);
                    parent.color = Color.BLACK;
                    grandFather.color = Color.RED;
                }
            }else{
                //parent == grandFather.right
                RBTreeNode uncle = grandFather.left;
                if(uncle != null && uncle.color == Color.RED){
                    uncle.color = parent.color = Color.BLACK;
                    grandFather.color = Color.RED;
                    if(grandFather == root){
                        grandFather.color = Color.BLACK;
                    }
                    cur = grandFather;
                    parent = cur.parent;
                }else{
                    //情况3
                    if (cur == parent.right){
                        rotateR(parent);
                        //交换
                        RBTreeNode tmp = cur;
                        cur = parent;
                        parent = tmp;
                    }
                    //情况2
                    rotateL(grandFather);
                    grandFather.color = Color.RED;
                    parent.color = Color.BLACK;
                }
            }
        }
        return true;
    }

    private void rotateR(RBTreeNode parent) {
        RBTreeNode subL = parent.left;
        RBTreeNode subLR = subL.right;
        parent.left = subLR;
        if(subLR != null){
            subLR.parent = parent;
        }
        subL.right = parent;
        RBTreeNode pParent = parent.parent;
        parent.parent = subL;
        if(root == parent){
            root = subL;
            root.parent = null;
        }else{
            if(parent == pParent.right){
                pParent.right = subL;
            }else{
                pParent.left = subL;
            }
            subL.parent = pParent;
        }
    }

    private void rotateL(RBTreeNode parent) {
        RBTreeNode subR = parent.right;
        RBTreeNode subRL = subR.left;
        parent.right = subRL;
        if(subRL != null){
            subRL.parent = parent;
        }
        subR.left = parent;
        RBTreeNode pParent = parent.parent;
        parent.parent = subR;
        if(parent == root){
            root = subR;
            root.parent = null;
        }else{
            if(parent == pParent.right){
                pParent.right = subR;
            }else{
                pParent.left = subR;
            }
            subR.parent = pParent;
        }
    }

    //中序遍历
    public void inOrder(RBTreeNode root){
        if(root == null){
            return;
        }
        inOrder(root.left);
        System.out.print(root.val + " ");
        inOrder(root.right);
    }

    //检测其是否满足红黑树的性质
    public boolean isValidRBTree(RBTreeNode root) {
        //如果是空树,满足
        if (root == null) {
            return true;
        }
        //根节点不是黑色的,不是红黑树
        if (root.color != Color.BLACK) {
            System.out.println("违反了红黑树的性质:根节点不是黑色");
            return false;
        }
        //获取单条路径中黑色节点的个数
        RBTreeNode cur = root;
        int blackCount = 0;
        while (cur != null) {
            if (cur.color == Color.BLACK) {
                blackCount++;
            }
            cur = cur.left;
        }
        return _isValidRBtree(root, 0, blackCount);
    }
    public boolean _isValidRBtree(RBTreeNode root, int pathCount, int blackCount){
        if(root == null){
            return true;
        }
        //遇到黑色的,pathCount++
        if(root.color == Color.BLACK){
            pathCount++;
        }
        RBTreeNode parent = root.parent;
        if(parent != null && parent.color == Color.RED && root.color == Color.RED){
            System.out.println("有连在一起的红色节点");
            return false;
        }
        if(root.left == null && root.right == null){
            if(pathCount != blackCount){
                System.out.println("路径中黑色节点数量不一致");
                return false;
            }
        }
        return _isValidRBtree(root.left, pathCount, blackCount) && _isValidRBtree(root.right, pathCount, blackCount);
    }
}

2.4AVL树和红黑树的比较

树类型AVL树红黑树
查找效率O(logN)O(logN)
特点追求绝对平衡,插入、删除需要进行大量旋转不追求绝对平衡,相对而言降低了插入和旋转的次数
在增删结构中性能比较相对较低更高的效率

2.5红黑树的应用

树类型AVL树红黑树
查找效率O(logN)O(logN)
特点追求绝对平衡,插入、删除需要进行大量旋转不追求绝对平衡,相对而言降低了插入和旋转的次数
在增删结构中性能比较相对较低更高的效率

2.5红黑树的应用

Java集合框架中:TreeMap、TreeMap底层使用的就是红黑树

评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值