java实现红黑树的插入节点

1.什么是红黑树

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

性质如下:

① 每一个节点或者着成红色或者着成黑色。

② 根节点为黑色。

③ 每个叶子节点为黑色。(指的是指针指向为NULL的叶子节点)

④ 如果一个节点是红色的,那么它的子节点必须是黑色的。

⑤ 从一个节点到一个NULL指针的每一条路径必须包含相同数目的黑色节点。

推论: 有n个节点的红黑树的高度最多是2log(N+1) 。

2.红黑树旋转操作的原理

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

右旋操作:


说明:将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 =null;
	}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);
}
左旋操作:


说明:将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 =null;
	}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);
}

3.红黑树插入元素的原理

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

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


解决办法:

由于新节点父节点为黑色,直接插入即可。


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


解决办法:

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

总结: 红父->黑父   红叔->黑叔   黑祖->红祖


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


解决办法:

这种情况同情况2一样,无须旋转,只是颜色不满足性质,只需调整颜色即可。

总结: 红父->黑父   红叔->黑叔   黑祖->红祖


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


解决办法:

这种情况单纯通过换色是无法控制红黑树性质的满足。通过右旋来满足平衡条件。

总结:右旋  红父->黑父  黑祖->红祖


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


解决办法:

这种使用上面的右旋会发现,就会出现两个值的节点和三个子节点的情况。我们先局部左旋父节点。然后将祖节点和叔节点右旋转。

总结:左旋  右旋  黑祖->红祖 红新->黑新


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


解决办法:

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

总结:红父->黑父   红叔->黑叔   黑祖->红祖


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


解决办法:

这种情况和情况3一样,都是只是调整下颜色就好。

总结:红父->黑父   红叔->黑叔   黑祖->红祖


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


解决办法:

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

总结:左旋  红父->黑父  黑祖->红祖


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


解决办法:

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

总结:右旋  左旋  黑祖->红祖  红新->黑新

4.全面总结

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

红叔,就调整颜色即可。

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

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

颜色枚举:

public enum Color {
	
	RED("0","红色"),
	BLACK("1","黑色");
	private String name = null;
	private String value =null;
	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 (null == 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 =null;  
      
    //顺时针右旋  
    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 =null;  
        }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 =null;  
        }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());*/
    }  
}  

测试用例:


测试结果:


6.问题


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

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

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


7.参考


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语言)





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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值