红黑树简介

概念性质

通过对任何红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是RedBlack。条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。(和AVL树不一样,红黑树只是接近平衡)

红黑树的性质
1.每个结点不是红色就是黑色
2.根节点是黑色的
3.如果一个节点是红色的,则它的两个孩子结点是黑色的(没有2个连续的红色节点)

4.对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点

5.每个叶子结点都是黑色的(此处的叶子结点指的是空结点)不能忽略的代表每一条路径

思考:一颗红黑树种,有X个黑色结点,这棵树总共有N个结点,那么N的取值范围是多少?

答:N∈[X,2X]

解析:这是根据红黑树的性质所的出来的,由于红色结点不能够连续有两个,所有最小的取值就是当N个结点全部为黑色结点的时候,没有一个红色结点;而最大的时候就是X个黑色结点排成一条最长路径,红色结点间隔插入(黑红结点间隔排列),这时黑色结点数=红色结点数

最短路径时间复杂度:logN

最长路径时间复杂度:logN+1

最短路径长度:logN

最长路径长度:2logN


代码

结点定义
 //枚举
 public enum COLOR {
     RED,BLACK
 }

 static class RBTreeNode {
        public RBTreeNode left;
        public RBTreeNode right;
        public RBTreeNode parent;
        public int val;
        public 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);
        node.color = COLOR.RED;
        if (root == null) {
            root = node;
            //如果是第一次插入的结点,要将其颜色设置为黑色
            root.color = COLOR.BLACK;
            return true;
        }
        RBTreeNode parent = null;
        RBTreeNode cur = root;
        while (cur != null) {
            if (cur.val < val) {
                parent = cur;
                cur = cur.right;
            } else if (cur.val > val) {
                parent = cur;
                cur = cur.left;
            } else {
                return false;
            }
        }
        if (parent.val > val) {
            parent.left = node;
        } else {
            parent.right = node;
        }
        cur = node;
        node.parent = parent;
//---------------------------------------------------------------------------------------
        //每插入一个结点,就需要调整颜色对于红黑树来说
        while (parent != null && parent.color == COLOR.RED) {
            RBTreeNode grandFather = parent.parent;
            if (parent == grandFather.left) {
                RBTreeNode uncle = grandFather.right;
                //第一种情况:uncle结点不为null且为红色
                if (uncle.color == COLOR.RED && uncle != null){
                    //将uncle结点和父亲结点变黑色,将祖父结点变红色
                    parent.color = COLOR.BLACK;
                    uncle.color = COLOR.BLACK;
                    grandFather.color = COLOR.RED;
                    //考虑祖父结点上还有其父结点,其复结点也可能是红色,所以仍需要向上查找
                    cur = grandFather;
                    parent = cur.parent;
                } else {
                    //第三种情况:parent为红,uncle为黑或者null
                    if (parent.right == cur) {
                        //先左旋再交换cur和parent的结点变成情况2,执行一样的逻辑即可
                        RotateR(parent);
                        //交换cur和parent结点
                        RBTreeNode tmp = parent;
                        parent = cur;
                        cur = tmp;
                    }
                    //第二种情况:parent结点为红 uncle结点为黑色或者null
                    //grandparent.left=parent parent.left=cur
                    //直接右旋
                    RotateR(grandFather);
                    //改变颜色
                    parent.color = COLOR.BLACK;
                    grandFather.color = COLOR.RED;
                }
            } else {
                //parent == grandfather.right
                RBTreeNode uncle = grandFather.left;
                //第一种情况:uncle为红色且不为null
                if (uncle.color == COLOR.RED && uncle == null) {
                    //改变uncle和parent grandfather的颜色
                    uncle.color = COLOR.BLACK;
                    parent.color = COLOR.BLACK;
                    grandFather.color = COLOR.RED;
                    cur = grandFather;
                    parent = grandFather.parent;
                } else {
                    //第三种情况:和上面逻辑一样,只是方向相反,右旋后交换结点,执行情况2的逻辑即可
                    if (cur == parent.left) {
                        //先右旋
                        RotateR(parent);
                        RBTreeNode tmp = parent;
                        parent = cur;
                        cur = tmp;
                    }
                    //第二种情况:uncle为null或者黑 parent为红
                    //grandfather.right = parent parent.right = cur
                    //左旋
                    RotateL(grandFather);
                    //改变grandfather和parent结点的颜色
                    grandFather.color = COLOR.RED;
                    parent.color = COLOR.BLACK;
                }
            }
        }
        root.color = COLOR.BLACK;
        return true;
    }

其插入的思想还是和搜索二叉树一样,定义一个结点cur指向当前root,定义一个parent结点指向cur.parent,当cur为null时,判断此时parent.val和插入结点val的值的大小来判断结点插入在parent结点的左边还是右边

后续的操作就是来修改红黑树的颜色,共有2类大情况,一类大情况有三种小情况

第一类大情况:grandFather.left=parent ----> RBTreeNode uncle=grandFather.right

     1.第一种小情况:(颜色就不写枚举了,直接写中文了,方便看)

        parent.color=红色 uncle.color=红色

       当grandFather变成红色后,其上面一定还有其父结点,如果其父结点也是红色的话,就

       违背了红黑树性质中的不能连续出现两个红色结点,所以我们仍然需要向上查找,这也

        对应了代码:cur = grandFather; parent = cur.parent

 //第一种情况:uncle结点和父结点颜色都为红色
                if (parent.color == COLOR.RED && uncle.color == COLOR.RED) {
                    //将uncle结点和父亲结点变黑色,将祖父结点变红色
                    parent.color = COLOR.BLACK;
                    uncle.color = COLOR.BLACK;
                    //考虑祖父结点上还有其父结点,其复结点也可能是红色,所以仍需要向上查找
                    cur = grandFather;
                    parent = cur.parent;

   2.第二种情况:

        parent.color=红色   uncle.color=黑色 or uncle=null

        此时我们需要判断cur与parent的相对位置

        (1):当cur = parent.left时,我们需要右旋,再改变parent和grandFather结点的颜色

       

       (2).当cur=parent.right时,我们需要先左旋,再交换parent和cur的结点,这时结构状态                就转变成情况2一样了,再执行情况2的逻辑即可

第二大类情况:grandFather.right=parent------>grandFather.left=uncle

其思想和前面的完全一样,只不过结构顺序相反,下面只画图分析

 第一种情况:

 

        

 第二种情况:

第三种情况:

  • 25
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值