红黑树的特点
- 每个节点或者是黑色节点,后者是红色节点
- 根节点是黑色节点
- 每个叶子节点(NULL节点)是黑色节点
- 若一个节点是红色节点,那么它的所有子节点都是黑色节点
- 从任意一个非叶子节点出发,到达叶子节点,其中所有路径经过的黑色节点的数目是相同的
以上5个特点决定红黑树中,从一个非叶子节点出发到达叶子节点,其中最长路径的长度不超过最短路径长度的两倍(由第5条性质,再加上第4条性质可以理解为红色节点不能连续出现,那么可以得出不超过两倍的结论)。
插入操作分析
这里插入操作借鉴一篇博客的内容,作者写的非常好。
首先约定插入的新节点的颜色都为红色。然后将该节点插入的按二叉查找树的规则插入到树中。这个节点后文称为N。
- 根节点为空。这种情况,将N的颜色改为黑色即可。
- N的父节点为黑色。这种情况不需要做修改。
- 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
最终结果如上图所示。