红黑树的插入操作详解

红黑树的插入操作详解

红黑树的特点

  1. 每个节点或者是黑色节点,后者是红色节点
  2. 根节点是黑色节点
  3. 每个叶子节点(NULL节点)是黑色节点
  4. 若一个节点是红色节点,那么它的所有子节点都是黑色节点
  5. 从任意一个非叶子节点出发,到达叶子节点,其中所有路径经过的黑色节点的数目是相同的
    以上5个特点决定红黑树中,从一个非叶子节点出发到达叶子节点,其中最长路径的长度不超过最短路径长度的两倍(由第5条性质,再加上第4条性质可以理解为红色节点不能连续出现,那么可以得出不超过两倍的结论)。

插入操作分析

这里插入操作借鉴一篇博客的内容,作者写的非常好。
首先约定插入的新节点的颜色都为红色。然后将该节点插入的按二叉查找树的规则插入到树中。这个节点后文称为N。

  1. 根节点为空。这种情况,将N的颜色改为黑色即可。
  2. N的父节点为黑色。这种情况不需要做修改。
  3. N的父节点为红色(根据性质3,N的祖父节点必为黑色)。

其中,第3种情况需要详细讨论:

  • N的叔父节点为红色。这种情况,将N的父节点和叔父节点的颜色都改为黑色,若祖父节点是跟节点就将其改为黑色,否则将其颜色改为红色,并以祖父节点为插入的目标节点从情况1开始递归检测。
    在这里插入图片描述

  • N的叔父节点为黑色, 且N和N的父节点在同一边(即父节点为祖父的左儿子时,N也是父节点的左儿子。父节点为祖父节点的右儿子时。N也是父节点的右儿子)。以父节点为祖父节的左儿子为例,将父节点改为黑色,祖父节点改为红色,然后以祖父节点为基准右旋。(N为父节点右儿子时做相应的左旋。)
    在这里插入图片描述

  • N的叔父节点为黑色,切N和N的父节点不在同一边(即父节点为祖父的左儿子时,N是父节点的右儿子。父节点为祖父节点的右儿子时。N也是父节点左右儿子)。以父节点为祖父节点的左儿子为例。以父节点为基准,进行左旋,然后以父节点为目标插入节点进入情况3的b情况进行操作。
    在这里插入图片描述

代码

public class RedBlackTreeInsert{
	private static final boolean RED=true;
	private static final boolean BLACK=false;
	private static class Node{//红黑树中每个节点的定义
		private int value;//节点中保存的元素,可以为任意类型
		private boolean color;//节点颜色
		private Node parent;//父节点引用
		private Node left;//左子节点引用
		private Node right;//右子节点引用
		public Node(int value){//构造函数
			this.value=value;
			this.parent=null;
			this.left=null;
			this.right=null;
			this.color=RED;
		}
	}
	public static boolean isRed(Node node){//判断是否为红色节点
		if(node!=null&&node.color==RED)//节点不是叶子节点 且 节点颜色是红色
			return true;
		return false;
	}
	public static boolean isBlack(Node node){
		return !isRed(node);
	}
	public static Node insert(Node root,int value){
		//	
	}
	public static void leftRoate(Node node){
		//
	}
	public static void rightRoate(Node node){
		//
	}
	public static Node adjust(Node root,Node n){
		//
	}
	public static void print(Node root){
		//
	}
	public static void main(String[] args){
		int[] a={1,0,2,4,5,6,7,8,9,10};
		Node root=null;
		for(int i=0;i<a.length;i++){
			root=insert(root,a[i]);
		}
		print(root);
	}
}
//这里给出insert函数的具体实现
public static Node insert(Node root,int value){
		if(root==null){//tree is empty,对应根节点为空的情况
			Node node=new Node(value);
			node.color=BLACK;
			return node;					
		}
		Node r=root,pre=null;
		while(r!=null){
			if(r.value>value)
				{pre=r;r=r.left;}
			else if(r.value<value)
				{pre=r;r=r.right;}
			else
				return root;
		}
		Node n=new Node(value);
		if(pre.value>value)
		{
			pre.left=n;
			n.parent=pre;
		}
		else{
			pre.right=n;
			n.parent=pre;
		}//上面是插入一个新的结点的代码
		return adjust(root,n);//插入后的调整操作		
	}
//这里是调整操作的完整代码
public static Node adjust(Node root,Node n){
		if(n.parent==null){//如果当前节点是根节点,那么变为黑色,返回。
			n.color=BLACK;
			return n;
		}
		if(n.parent.parent==null)//当前节点没有祖父节点,返回根节点
			return n.parent;
		else{//如果有祖父节点,即按照第三种情况进行调整
			if(n.parent.color==BLACK)//父节点是黑色
				return root;
			else{//父节点是红色
				if(n.parent.parent.left==n.parent){//父节点是祖父节点的左子节点
					if(isRed(n.parent.parent.right)){//叔父节点是红色
						n.parent.color=BLACK;
						n.parent.parent.right.color=BLACK;
						n.parent.parent.color=RED;//变色
						return adjust(root,n.parent.parent);//向上递归调整
					}
					else{
						if(n.parent.left!=n){//子节点与其父节点不在同一边
							leftRoate(n.parent);
						}
						n.parent.color=BLACK;
						n.parent.parent.color=RED;
						if(n.parent.parent==root){//判断旋转操作后,根节点是否发生变化
							rightRoate(n.parent.parent);
							return n.parent;
						}
						else
							rightRoate(n.parent.parent);
					}	
			}
			else{//父节点是祖父节点的右子节点
				if(isRed(n.parent.parent.left)){
					n.parent.color=BLACK;
					n.parent.parent.left.color=BLACK;
					n.parent.parent.color=RED;
					return adjust(root,n.parent.parent);
				}
				else{
					if(n.parent.right!=n){
						rightRoate(n.parent);
					}
					n.parent.color=BLACK;
					n.parent.parent.color=RED;
					if(n.parent.parent==root){
						leftRoate(n.parent.parent);
						return n.parent;
					}
					else
						leftRoate(n.parent.parent);	
				}
				
			}
			return root;//旋转后根节点不发生变化的情况
		}		
	}
}
//左旋操作的完整代码
public static void leftRoate(Node node){
		Node tmp=node.right;
		node.right=tmp.left;
		if(node.right!=null)
			node.right.parent=node;
		Node parent=node.parent;
		tmp.left=node;
		node.parent=tmp;
		tmp.parent=parent;
		if(parent!=null)
		if(parent.left==node)
			parent.left=tmp;
		else
			parent.right=tmp;	
	}
//右旋操作的完整代码
public static void rightRoate(Node node){
		Node tmp=node.left;
		node.left=tmp.right;
		if(node.left!=null)
			node.left.parent=node;
		Node parent=node.parent;
		tmp.right=node;
		node.parent=tmp;
		tmp.parent=parent;
		if(parent!=null)
		if(parent.left==node)
			parent.left=tmp;
		else
			parent.right=tmp;
	}
//print函数的完整代码,用于按层遍历红黑树
public static void print(Node root){
		if(root==null) return;
		Queue<Node> queue=new ArrayBlockingQueue<Node>(100);
		queue.offer(root);
		String str;
		while(queue.size()>0){
			Node n=queue.poll();
			if(n.value==-1)//如果节点为叶子节点,通过value值=-1,表示其是叶子节点
			{
				System.out.println("null");//叶子节点输出null,注意叶子节点不会有子节点
				continue;
			}
			if(n.color==BLACK) str="black(";
			else str="red(";
			str+=n.value;
			str+=")";
			System.out.println(str);
			if(n.left!=null) queue.offer(n.left); else queue.offer(new Node(-1));
			if(n.right!=null) queue.offer(n.right); else queue.offer(new Node(-1));
		}
	}
最终的输出结果:
black(4)
black(1)
black(6)
black(0)
black(2)
black(5)
red(8)
null
null
null
null
null
null
black(7)
black(9)
null
null
null
red(10)
null
null

最终结果构成的红黑树
最终结果如上图所示。

发布了8 篇原创文章 · 获赞 1 · 访问量 58

猜你喜欢

转载自blog.csdn.net/qq_33898680/article/details/103922497