红黑树插入节点

什么是红黑树

红黑树是带有着色性质的二叉查找树。

性质如下:
① 每一个节点或者着成红色或者着成黑色。
② 根节点为黑色。
③ 每个叶子节点为黑色。(指的是指针指向为NULL的叶子节点)
④ 如果一个节点是红色的,那么它的子节点必须是黑色的。
⑤ 从一个节点到一个NULL指针的每一条路径必须包含相同数目的黑色节点。
推论: 有n个节点的红黑树的高度最多是2log(N+1) 。

红黑树旋转操作的原理

下述代码注释搭配图片便于理解

右旋操作:

1.jpg

说明:将rbn节点和c节点顺时针旋转到b的下面,然后调整位置。
代码实现:

//顺时针右旋  
public static void rotateByRight(RBNode rbn){  
    RBNode b =rbn.getLchild();  
    //旋转后结果调整,rbn的左孩子为e  
    rbn.setLchild(b.getRchild());  
    //如果b的右孩子e存在就确定e的父节点为rbn  
    if(b.getRchild() !=null){  
        b.getRchild().setParent(rbn);  
    }  
    //确定b的父节点为原rbn的父节点  
    b.setParent(rbn.getParent());  
    if(rbn.getParent() ==null){  
        root =;  
    }else{  
        //确定rbn是否为父节点的右孩子节点  
        if(rbn ==rbn.getParent().getRchild()){  
            rbn.getParent().setRchild(b);  
        }else{  
            rbn.getParent().setLchild(b);  
        }  
    }  
    //确定b的右孩子为rbn  
    b.setRchild(rbn);  
    //确定rbn的父节点为b  
    rbn.setParent(b);  
}  

左旋操作:

2.jpg

说明:将rbn节点和c节点逆时针旋转到d的下面,然后调整位置。

代码实现:

//逆时针左旋  
public static void rotateByLeft(RBNode rbn){  
    RBNode d =rbn.getRchild();  
    //确定rbn的右孩子为e  
    rbn.setRchild(d.getLchild());  
    //如果e不为空,那么确定e的父节点为rbn  
    if(d.getLchild() !=null){  
        d.getLchild().setParent(rbn);  
    }  
    //确定d的父节点为rbn的父节点  
    d.setParent(rbn.getParent());  
    if(rbn.getParent() ==null){  
        root =;  
    }else{  
        //确定rbn是否为其父节点的左孩子节点,目得是为了确定d的父节点的左孩子还是右孩子为d  
        if(rbn ==rbn.getParent().getLchild()){  
            rbn.getParent().setLchild(d);  
        }else{  
            rbn.getParent().setRchild(d);  
        }  
    }  
    //确定d的左孩子为rbn  
    d.setLchild(rbn);  
    //确定rbn的父节点为d  
    rbn.setParent(d);  
}  

红黑树插入元素的原理

关于红黑树插入节点后为了保持红黑树的特性,主要进行的操作就是换颜色和旋转操作。

情况1:父节点是黑色,插入新节点为红色。

3.png

解决办法:
由于新节点父节点为黑色,直接插入即可。

情况2:父节点为红色,叔叔节点为红色,插入新节点为红色,插入新节点为父节点的左孩子节点。(红叔左父左插入情况)

4.png

解决办法:
为了保持新节点为红色,如果此时父节点为红色就不满足性质4.所以父节点需要调整为黑色,但是这样祖节点到NL叶子节点,就会出现数目不同的黑色节点了。不满足性质5了。所以祖节点需要调整为红色节点满足性质。这样叔叔也就得调整为黑色了,要不就不满足性质4和性质5.
总结:红父->黑父 红叔->黑叔 黑祖->红祖

情况3:父节点为红色,叔叔节点为红色,插入新节点为红色,插入新节点为父节点的右孩子节点。(红叔左父右插入情况)

5.png

解决办法:
这种情况同情况2一样,无须旋转,只是颜色不满足性质,只需调整颜色即可。
总结: 红父->黑父 红叔->黑叔 黑祖->红祖

情况4:父节点为红色,叔叔节点为黑色,插入新节点为红色,插入新节点为父节点的左孩子节点。(黑叔左父左插入情况)

6.png

解决办法:
这种情况单纯通过换色是无法控制红黑树性质的满足。通过右旋来满足平衡条件。
总结:右旋 红父->黑父 黑祖->红祖

情况5:父节点为红色,叔叔节点为黑色,插入新节点为红色,插入新节点为父节点的右孩子节点。(黑叔左父右插入情况)

7.png

解决办法:
这种使用上面的右旋会发现,就会出现两个值的节点和三个子节点的情况。我们先局部左旋父节点。然后将祖节点和叔节点右旋转。
总结:左旋 右旋 黑祖->红祖 红新->黑新

情况6:父节点为红色,叔叔节点为红色,插入新节点为红色,插入新节点为父节点的左孩子节点。(红叔右父左插入情况)

8.png

解决办法:
这种情况和情况2差不多,就是调整颜色就可以了,主要原因就可以发现父节点和叔叔节点都是红色,这样直接调整颜色,就会满足红黑树性质。
总结:红父->黑父 红叔->黑叔 黑祖->红祖

情况7:父节点为红色,叔叔节点为红色,插入新节点为红色,插入新节点为父节点的右孩子节点。(红叔右父右插入情况)

9.png

解决办法:
这种情况和情况3一样,都是只是调整下颜色就好。
总结:红父->黑父 红叔->黑叔 黑祖->红祖

情况8:父节点为红色,叔叔节点为黑色,插入新节点为红色,插入新节点为父节点的右孩子节点。(黑叔右父右插入情况)

10.png

解决办法:
这种情况会出现旋转了,因为叔节点和父节点颜色不同,所以单纯调整颜色不能满足性质了。就采用将祖节点和叔节点左旋,作为父节点的左孩子树。
总结:左旋 红父->黑父 黑祖->红祖

情况9:父节点为红色,叔叔节点为黑色,插入新节点为红色,插入新节点为父节点的左孩子节点。(黑叔右父左插入情况)

11.png

解决办法:
这种情况类似于情况5,如果我们直接左旋祖节点和叔节点,那么就会出现2节点,3孩子情况。所以为了避免这种冲突。就先进行局部旋转,先将父节点右旋,然后将祖节点和叔节点左旋。
总结:右旋 左旋 黑祖->红祖 红新->黑新

全面总结

黑父,直接插入新节点即可。

红叔,就调整颜色即可。

黑叔,需要进行旋转操作。

红黑树插入元素完整代码实现

颜色枚举:

public enum Color {  
      
    RED("0","红色"),  
    BLACK("1","黑色");  
    private String name = ;  
    private String value =;  
    private Color(String name, String value) {  
        this.name=name;  
        this.value=value;  
    }  
    public String getName() {  
        return name;  
    }  
    public void setName(String name) {  
        this.name = name;  
    }  
    public String getValue() {  
        return value;  
    }  
    public void setValue(String value) {  
        this.value = value;  
    }  
    public static Color getEnumByName(String name){  
        if ( == name) {  
            return null;  
        }  
        for (Color type : values()) {  
            if (type.getName().equals(name.trim()))  
                return type;  
        }  
        return null;  
    }  
      
    public static Map<String, String> toMap() {  
        Map<String, String> enumDataMap = new LinkedHashMap<String, String>();  
        for (Color type : values()) {  
            enumDataMap.put(type.getName(), type.getValue());  
        }  
        return enumDataMap;  
    }  
      
}  

红黑树节点数据的结构定义:

public class RBNode {  
  
    private Integer data;  
    //红黑树中节点对应的颜色  
    private Color color;  
    //红黑树中当前节点的左孩子节点  
    private RBNode lchild;  
    //红黑树中当前节点的右孩子节点  
    private RBNode rchild;  
    //红黑树中当前节点的父节点  
    private RBNode parent;  
    public RBNode(){}  
    public RBNode(Integer data){  
        this.data =data;  
    }  
    public RBNode(Integer data,Color color,RBNode parent,RBNode lchild,RBNode rchild){  
        this.data =data;  
        this.color =color;  
        this.parent =parent;  
        this.lchild =lchild;  
        this.rchild =rchild;  
    }  
      
    public RBNode getParent() {  
        return parent;  
    }  
    public void setParent(RBNode parent) {  
        this.parent = parent;  
    }  
    public Integer getData() {  
        return data;  
    }  
    public void setData(Integer data) {  
        this.data = data;  
    }  
    public Color getColor() {  
        return color;  
    }  
    public void setColor(Color color) {  
        this.color = color;  
    }  
    public RBNode getLchild() {  
        return lchild;  
    }  
    public void setLchild(RBNode lchild) {  
        this.lchild = lchild;  
    }  
    public RBNode getRchild() {  
        return rchild;  
    }  
    public void setRchild(RBNode rchild) {  
        this.rchild = rchild;  
    }  
}  

主程序代码:

public class RBTree {    
      
    private static RBNode root =;    
        
    //顺时针右旋    
    public static void rotateByRight(RBNode rbn){    
        RBNode b =rbn.getLchild();    
        rbn.setLchild(b.getRchild());    
        if(b.getRchild() !=null){    
            b.getRchild().setParent(rbn);    
        }    
        b.setParent(rbn.getParent());    
        if(rbn.getParent() ==null){    
            root =;    
        }else{    
            if(rbn ==rbn.getParent().getRchild()){    
                rbn.getParent().setRchild(b);    
            }else{    
                rbn.getParent().setLchild(b);    
            }    
        }    
        b.setRchild(rbn);    
        rbn.setParent(b);    
    }    
        
    //逆时针左旋    
    public static void rotateByLeft(RBNode rbn){    
        RBNode d =rbn.getRchild();    
        rbn.setRchild(d.getLchild());    
        if(d.getLchild() !=null){    
            d.getLchild().setParent(rbn);    
        }    
        d.setParent(rbn.getParent());    
        if(rbn.getParent() ==null){    
            root =;    
        }else{    
            if(rbn ==rbn.getParent().getLchild()){    
                rbn.getParent().setLchild(d);    
            }else{    
                rbn.getParent().setRchild(d);    
            }    
        }    
        d.setLchild(rbn);    
        rbn.setParent(d);    
    }    
        
        
        
    //红黑树插入节点    
    private static void insertRBNode(RBNode insertNode){    
        RBNode tempRoot =root;    
        //给红黑树插入节点,先不考虑局部平衡问题    
        while(tempRoot !=null){    
            if(insertNode.getData()<tempRoot.getData()){    
                if(tempRoot.getLchild() !=null){    
                    tempRoot =tempRoot.getLchild();    
                }else{    
                    tempRoot.setLchild(insertNode);    
                    insertNode.setParent(tempRoot);    
                    break;    
                }    
            }else if(insertNode.getData()>tempRoot.getData()){    
                if(tempRoot.getRchild() !=null){    
                    tempRoot =tempRoot.getRchild();    
                }else{    
                    tempRoot.setRchild(insertNode);    
                    insertNode.setParent(tempRoot);    
                    break;    
                }    
            }    
        }    
        //插入节点设置为红色    
        insertNode.setColor(Color.RED);    
        insertNode.setLchild(null);    
        insertNode.setRchild(null);    
        adjustRBTree(insertNode);    
    }    
        
    //调整红黑树    
    public static void adjustRBTree(RBNode rbNode){    
        //定义父节点    
        RBNode parent =rbNode.getParent();    
        while(parent !=null && parent.getColor().equals(Color.RED)){    
            //定义祖父节点    
            RBNode grandParent =parent.getParent();    
            //如果父节点是祖父节点的左孩子    
            if(parent.equals(grandParent.getLchild())){    
                RBNode uncleNode =grandParent.getRchild();    
                //情况2、情况3:红叔    
                if(uncleNode !=null && uncleNode.getColor().equals(Color.RED)){    
                    uncleNode.setColor(Color.BLACK);    
                    parent.setColor(Color.BLACK);    
                    grandParent.setColor(Color.RED);    
                }else if(uncleNode !=null && uncleNode.getColor().equals(Color.BLACK) && parent.getLchild().equals(rbNode)){    
                    //情况4:黑叔,当前节点是左孩子    
                    rotateByRight(grandParent);    
                    parent.setColor(Color.BLACK);    
                    grandParent.setColor(Color.RED);    
                }else if(uncleNode !=null && uncleNode.getColor().equals(Color.BLACK) && parent.getRchild().equals(rbNode)){    
                    //情况5:黑叔,当前节点是右孩子    
                    rotateByLeft(parent);    
                    rotateByRight(grandParent);    
                    rbNode.setColor(Color.BLACK);    
                    grandParent.setColor(Color.RED);    
                }else{  
                    break;  
                }  
            }else{//如果父节点是祖父节点的右孩子    
                RBNode uncleNode =grandParent.getLchild();    
                //情况6、情况7:红叔    
                if(uncleNode !=null && uncleNode.getColor().equals(Color.RED)){    
                    uncleNode.setColor(Color.BLACK);    
                    parent.setColor(Color.BLACK);    
                    grandParent.setColor(Color.RED);    
                }else if(uncleNode !=null && uncleNode.getColor().equals(Color.BLACK) && parent.getRchild().equals(rbNode)){    
                    //情况8:黑叔,当前节点是右孩子    
                    rotateByLeft(grandParent);    
                    parent.setColor(Color.BLACK);    
                    grandParent.setColor(Color.RED);    
                }else if(uncleNode !=null && uncleNode.getColor().equals(Color.BLACK) && parent.getLchild().equals(rbNode)){    
                    //情况9:黑叔,当前节点是左孩子    
                    rotateByRight(parent);    
                    rotateByLeft(grandParent);    
                    grandParent.setColor(Color.RED);    
                    rbNode.setColor(Color.BLACK);    
                }else{  
                    break;  
                }  
            }    
        }    
        root.setColor(Color.BLACK);    
    }    
        
    public static void queryRBNodeByPre(RBNode root){    
        if(root !=null){    
            System.out.print(root.getData()+"["+root.getColor().getValue()+"]"+" - ");    
            queryRBNodeByPre(root.getLchild());    
            queryRBNodeByPre(root.getRchild());    
        }else{    
            return;    
        }    
    }    
        
    /*递归方式遍历红黑树    
     * root:为遍历红黑树的根节点    
     * 中序方式    
     * */    
    public static void queryRBNodeByOrder(RBNode root) {    
        if(root !=null ){    
            queryRBNodeByOrder(root.getLchild());    
            System.out.print(root.getData()+"["+root.getColor().getValue()+"]"+" - ");    
            queryRBNodeByOrder(root.getRchild());    
        }else{    
            return;    
        }    
    }    
   
    public static void main(String[] args) {    
        root =new RBNode(13,Color.BLACK,null,null,null);    
        insertRBNode(new RBNode(8));    
        insertRBNode(new RBNode(17));    
        insertRBNode(new RBNode(1));    
        insertRBNode(new RBNode(11));    
        insertRBNode(new RBNode(15));    
        insertRBNode(new RBNode(25));    
        insertRBNode(new RBNode(6));    
        insertRBNode(new RBNode(22));    
        insertRBNode(new RBNode(27));    
        /*initRBTree(new RBNode(6), root);    
        initRBTree(new RBNode(5), root);    
        initRBTree(new RBNode(4), root);    
        queryRBNodeByOrder(root);    
        System.out.println();    
        queryBSTNodeByPre(root);    
        System.out.println();    
        System.out.println("旋转值:");    
        //rotateByLeft(root.getLchild());    
        rotateByRight(root.getRchild());*/    
        queryRBNodeByPre(root);    
        System.out.println();    
        System.out.println("----------------");    
        queryRBNodeByOrder(root);   
          
        /*RBNode rbNode =getMinRchild(root);  
        System.out.println(rbNode.getData());*/  
    }    
}    

测试用例

12.jpg

测试结果

13.png

问题

在插入节点的时候,我直接使用root全局变量来操作的,发现程序的根节点被替换了,程序出现了问题。

后来才想到全局变量是在堆中开辟空间的,而堆是共享区域。方法体内定义的变量是在栈中开辟空间的,是在每个线程私有的区域,如果为了防止全局变量被修改,那么在方法中调用全局变量时,可以单独复制一份,以防止出现全局变量被修改。

如果读者发现博客中出现问题,谢谢评论指出。

参考

http://www.cnblogs.com/dongritengfei/archive/2010/06/16/1759209.html

http://www.cnblogs.com/skywang12345/p/3245399.html#commentform

https://www.ibm.com/developerworks/cn/java/j-lo-tree/index.html?ca=drs-

数据结构与算法分析(c语言)

©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容

  • 树的概述 树是一种非常常用的数据结构,树与前面介绍的线性表,栈,队列等线性结构不同,树是一种非线性结构 1.树的定...
    Jack921阅读 4,432评论 1 31
  • R-B Tree简介 R-B Tree,全称是Red-Black Tree,又称为“红黑树”,它一种特殊的二叉查找...
    张晨辉Allen阅读 9,230评论 5 30
  • 1、红黑树介绍 红黑树又称R-B Tree,全称是Red-Black Tree,它是一种特殊的二叉查找树,红黑树的...
    文哥的学习日记阅读 9,851评论 1 35
  • 1.定义 红黑树是特殊的二叉查找树,又名R-B树(RED-BLACK-TREE),由于红黑树是特殊的二叉查找树,即...
    遇见技术阅读 16,100评论 0 25
  • 0.目录 1.算法导论的红黑树本质上是2-3-4树 2.红黑树的结构和性质 3.红黑树的插入 4.红黑树的删除 5...
    王侦阅读 2,460评论 1 2