谈谈红黑树

目录

 

1.红黑树是啥?

2.为什么红黑树能有高效率?

3.红黑树调整原理说明

4.java代码实现


1.红黑树是啥?

       初次接触到红黑树是在hashmap的源码分析,hashmap是一种查找以及删除效率较高的数据结构,但是在单个桶位链表过长(冲突发生了很多次)的情况下hashmap的效率会降低,极端一点,整个hashmap只有一个链表存在的情况下(假设不存在树化机制),它就变成了一个链表。

       为了在多数据的情况下提高效率,hashmap选择在单链表长度达到8时将其转化为红黑树。

       树结构在插入,删除,查找都有不错的效率,但是一般的二叉树也存在问题,如果左子树和右子树差距过大,那么树的性能也会降低。所以,为了保证树的性能,要让他的子树差距小,进而我们需要按照某些规则向树插入节点。红黑树在二叉树的基础上依靠以下规则维持左子树和右子树的平衡。

  1. 树中的节点要么是红色,要么是黑色。       
  2. 根节点是黑色,叶子节点是黑色。(但是这里指的叶子节点是空节点)
  3. 如果一个节点是红色,那么它的子节点是黑色。
  4. 任意节点到其叶子节点的所有路径都包含相同数目的黑色节点。

(插入的节点都是红色,这是为了防止打乱第四条规则) 

  

2.为什么红黑树能有高效率?

         对于树而言,查找,插入,删除的成本正比于查找路径。不难判断,上述红黑树的规则是用来限定查找路径长度的。

        那么它如何限制?

        如果我们要访问某一个叶子节点。考虑两种极端情况。

       情况一:整个访问路径上没有任何的红色节点,那么长度就是黑色节点的长度。(可以看到规则四,因为到每一个叶子节点的黑色节点数目相同,那么黑色节点的长度是不可避免的,所有只有黑色节点的路径是最短路径)

       情况二:路径上有尽可能多的红色节点与黑色节点,因为规则三的限制,所以红色节点只能插在黑色节点中间,他们不能连续。此时路径就是黑色节点长度的两倍。

      

               

        综上所述,最长路径不能超越最短路径的两倍。这也保证红黑树的左右子树差距一定不会过大,查找的稳定性强。

       (PS:笔者在查阅资料时看到一种比较硬核的证明方式:https://www.cnblogs.com/skywang12345/p/3245399.html#!comments

3.红黑树调整原理说明

       节点的调整有两种方式,一种是左旋,一种是右旋,虽然只有两种,但是他们也成功的涵盖了所有可能接触到的节点变化(这个变化有的前提条件便是变化符合二叉树的规则)。(这个其实可以用数学归纳法推理,以后有时间补上。)

       右旋:  将节点变为左子节点的右节点。      

       假设对B进行右旋。

                                                      

       左旋:将节点右子节点的左节点。

        同样,对B进行左旋

                                           

        假设三个节点,cur为需要插入的节点,parent节点为父节点,gpa节点为祖父节点,uncle节点叔叔节点

        1.情况一:uncle节点为空,parent节点红色,cur节点为父节点的左子节点         

                                                        

       此时的处理步骤为:1.将gpa节点右旋(gpa作为parent节点的右孩子存在)

                                                           

                                      2.修改节点颜色

                                                          

 

    2.情况2:cur节点为parent 节点的右子节点,uncle为空

                                                              

                      1.将parent节点左旋,(相对于情况一,仅仅只是多了这一步,之后的步骤都一样)

                                                             

                     2.之后的步骤和情况一一样

 

 3.情况3,4:叔叔节点存在,而且为红色,(分为不同情况仅仅是因为与gpa的关系可能不同)

                                                

              直接变色,完了之后在gpa节点继续进行调整

                                                         

4.情况5,6:叔叔节点为黑色(这个情况的处理模式笔者一直觉得有多种情况,反正还是仁者见仁,智者见智)

                                               

                   将gpa节点右旋,然后将gpa修改为红色,parent变为黑色(感觉与直接将gpa变为红色,然后parent变为黑色没有任何区别,仅仅只是不需要去循环调整)

                                                          

 

大概的情况也就以上几种了。                                 

                                               

                                   

4.java代码实现

  调整方法代码:存储数据结构类似于map(key,value),所有的排序根据key进行,(key类作为泛型存在,他继承了Comparable方法)

   

private void adjustNode2(Node node){
        Node curNode=node;
        Node parent=node.parent;
        Node gpa;

        while (parent!=null && parent.isRed){
            //祖父节点是否存在
            if ((gpa=parent.parent)==null){
                //不存在即证明parent节点为根节点
                break;
            }
            //父节点的位置
            if (gpa.leftCh==parent){
                //父节点是祖父节点的左子节点
                Node uncle;
                if ((uncle=gpa.rightCh)==null){
                    //没有叔叔节点
                    //判断子节点位置
                    if(curNode==parent.leftCh){
                        //如果是左孩子
                        //那么gpa右旋(作为左子节点的右孩子(这里就是parent的右孩子))
                        gpa.isRed=true;
                        parent.isRed=false;
                        RightRotation(gpa);
                        //调整完毕时调整的三层树结构的顶端(此时为curNode)
                        //为黑色,且叶子节点为红色,那么不会影响树结构
                        //所以直接跳出
                        break;
                    }else {
                        //右孩子
                        gpa.isRed=true;
                        curNode.isRed=false;
                        LeftRotation(parent);
                        RightRotation(gpa);
                        break;//同上
                    }
                }else{
                    //uncle存在
                    if (uncle.isRed){
                        //这种情况下,不论curNode的位置
                        //直接将gpa变为红色
                        //parent和uncle变为黑色
                        //然后重新调整gpa节点就好
                        uncle.isRed=false;
                        parent.isRed=false;
                        gpa.isRed=true;
                        curNode=gpa;
                    }else{
                        //叔叔节点为黑色
                        parent.isRed=false;
                        gpa.isRed=true;
                        RightRotation(gpa);
                        break;
                    }
                }
            }else{
                //父节点是祖父节点的右子节点
                //同上
                Node uncle;
                if ((uncle=gpa.leftCh)==null){
                    if (curNode==parent.rightCh){
                        parent.isRed=false;
                        gpa.isRed=true;
                        LeftRotation(gpa);
                        break;
                    }else{
                        curNode.isRed=false;
                        gpa.isRed=true;
                        RightRotation(parent);
                        LeftRotation(gpa);
                        break;
                    }
                }else{
                    if (uncle.isRed){
                        gpa.isRed=true;
                        parent.isRed=false;
                        uncle.isRed=false;
                        curNode=uncle;
                    }else{
                        parent.isRed=false;
                        gpa.isRed=true;
                        LeftRotation(gpa);
                        break;
                    }
                }
            }
        }

        //左旋右旋的过程中会去修改根节点
        root.isRed=false;
    }

 

 

以上,如果有什么错误,欢迎指出.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值