目录
一.概念
红黑树本质是一种二叉搜索树,是AVL树的特化版本。其也是通过一些条件限制来达到相对平衡的目的。
定义:
1.节点是红色或黑色;
2.根节点是黑色;
3.所有叶子节点都是黑色;
4.每个红色节点的两个子节点都是黑色,不能有两个连续的红色节点;
5.从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。
红黑树必须满足上面的五条规则,满足上述的五条规则即可实现:最长路径最多是最短路径的2倍 这一平衡。
二.定义节点
这里定义的节点与二叉搜索树大致相同,多了一个颜色标识。这里的颜色大家可以使用自己熟悉的方法,像布尔变量,定义一个flag.....都行,这一使用的是枚举。
一个节点在初始的时候是红色的,这是定义导致的。定义第五条规定了每条路上的黑色节点数要相同,如果插入一个黑色节点,那么要调整的节点很多很复杂。只要插入了,百分之一百要调整(数都变了)。如果插入红色节点,要调整的内容小很多,有时甚至不用调(parent节点是黑色)。
static class TreeNode{
public int val;
public TreeNode left;
public TreeNode right;
public TreeNode parent;
public Color color;
public TreeNode(int val){
this.val=val;
this.color=Color.RED;
}
}
public TreeNode root;
三.插入操作
插入节点的操作与二叉搜索树的插入没有区别,区别是后面对颜色的调整。
颜色调整的准则就是上面的五条定义。
这里可以进行分类讨论:
1.插入的是根节点,直接染上黑色返回即可;
2.插入点的parent是黑色,由于插入点默认是红色,所以不影响定义5,可以直接结束;
3.插入点的parent是红色,由于parent是红色,可得grandparent一定是黑色,否则就不满足性质4了。所以说我们只需要讨论插入点的uncle节点是什么颜色的即可。这里可以分出三种:
第一种:uncle是红色的;第二种:uncle是黑色或空的。
如果是第一种,让该点的parent节点和uncle节点颜色变黑,grandparent节点的颜色变红,然后让cur(插入点,也可能是循环过程中的点)指向grandparent,让parent指向grandparent的parent,再次进入循环。
如果是第二种,情况复杂很多,这里要画图方便理解。
这是cur是parent的左子树的情况:
上图是循环过程中的某一次,其可能是完整的树也可能是某一颗子树。
我们可以先染色后右旋(也可以先右旋后染色,这个无所谓)。将grandparent染成红色,parent染成黑色:
染完色后要对grandparent为根的树进行右旋:
这时树就平衡了。
还有一种是cur是parent的右树的时候:
这种情况其实是上一种的变种,将以parent为根的树进行左旋:
是不是很熟悉,如果不熟悉去看一下上面cur是parent左子树的开始状态,不正是跟这个图的颜色一样,只需将cur和parent交换一下,就可以转化成cur是parent左子树这种情况了。
这里我们忽略了一种情况,如果我们不小心将根节点改成红色怎么办?
这里我们最后直接将根节点变成黑色的就行。原因:定义2。可行性:任意一条路径都经过根节点,所以染黑不影响平衡的规则。
除了上面的这种情况,还有一种就是对称情况,将左右反转过来。这种情况与上面思路相同,只需将上面涉及左右的节点换一下相反的即可,这里就不过多赘述了。
public boolean insert(int val){
TreeNode node = new TreeNode(val);
if(root == null){
root=node;
root.color=Color.BLACK;
//插入成功,直接结束
return true;
}
//寻找插入的位置
TreeNode cur=root;
TreeNode parent=null;
while(cur!=null){
if(cur.val<val){
parent=cur;
cur=cur.right;
}else if(cur.val>val){
parent=cur;
cur=cur.left;
}else{
//已经有这个点了
return false;
}
}
//开始插入
cur=node;
node.parent=parent;
if(parent.val>val){
parent.left=cur;
}else{
parent.right=cur;
}
//调整红黑树
if(parent.color==Color.BLACK){
return true;
}
while(parent!=null && parent.color==Color.RED){
//父节点为红,祖父节点一定是黑,只需讨论叔节点
TreeNode grandParent = parent.parent;
TreeNode uncle = null;
if(parent==grandParent.left){
uncle = grandParent.right;
if(uncle!=null && uncle.color==Color.RED){
//叔节点为红
parent.color=uncle.color=Color.BLACK;
grandParent.color=Color.RED;
cur=grandParent;
parent=cur.parent;
}else{
//叔节点不为红或为空
//cur是parent的右孩子
if(cur==parent.right){
rotateLeft(parent);
TreeNode tem=parent;
parent=cur;
cur=tem;
}
grandParent.color=Color.RED;
parent.color=Color.BLACK;
rotateRight(grandParent);
}
}else{
uncle = grandParent.left;
if(uncle!=null && uncle.color==Color.RED){
//叔节点为红
parent.color=uncle.color=Color.BLACK;
grandParent.color=Color.RED;
cur=grandParent;
parent=cur.parent;
}else{
//叔节点不为红或为空
//cur是parent的左孩子
if(cur==parent.left){
rotateRight(parent);
TreeNode tem=parent;
parent=cur;
cur=tem;
}
grandParent.color=Color.RED;
parent.color=Color.BLACK;
rotateLeft(grandParent);
}
}
}
root.color=Color.BLACK;
return true;
}
以上就是插入操作的全部内容了。
四.总结
红黑树是相对平衡的,没有像AVL树有很多插入删除操作,性能相对AVL树更强,实际应用也很多,像Java中TreeMap底层就用的是红黑树。