1 简介
1.2 特性
- (红或黑)每个节点或者是黑色,或者是红色
- (根是黑)根节点是黑色
- (叶是黑)每个叶子节点是黑色(叶子节点只为空)
- (红子黑)如果一个节点是红色的,则它的子节点必须是黑色的
- (黑数同)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点
1.3 完全黑平衡
最长路径的节点中数量不会超过最短路径的两倍
1.4 应用
TreeSet\TreeMap\Linux虚拟内存
1.5 时间复杂度O(lgn)
一颗含有n个节点的红黑树的高度至多为2log(n+1)
2 基本操作
2.1 旋转
原来左小右大的特点不会受到影响,但右旋左子树高度减一,右子树加一,左旋相反
2.1.1 左旋
将X变成一个左节点,Y的左孩子变成X的右孩子,Y占据原来X节点相对其父节点的位置
2.1.2 右旋
将Y变成一个右节点,X的右孩子变成Y的左孩子,X占据原来Y节点相对其父节点的位置
2.2 添加
2.2.1 插入
将红黑树当做一颗二叉查找树,将节点插入
2.2.2 着色
将插入的节点着色为“红色“(黑色的话一定会违背特性特性5黑数同,红色的话可能会违背特性4红子黑)
2.2.3 平衡
2.2.3.1 N为根
N(当前节点)涂黑
2.2.3.2 父为黑
无
2.2.3.3 父红叔红
P(父亲节点)和U(叔叔节点)涂黑,GP(祖父节点)涂红,以GP作为新N,递归处理
2.2.3.4 父红叔黑
(父和N)同左
以GP右旋,原P涂黑,原GP涂红
同右
以GP左旋,原P涂黑,原GP涂红
父左N右
以P左旋,转至同左
父右N左
以P右旋,转至同右
2.3 删除
2.3.1 二叉树删除
将红黑树当成一个二叉查找树,将节点删除
2.3.1.1 无子节点
如果为红色,直接删除;否则,删除后进行平衡(唯一引发平衡操作的地方)
2.3.1.2 只有一个子节点
删除节点只能是黑色,其子节点为红色(因为红色节点的子节点不可能只有一个),此时删除节点的子节点接到父节点上,且将子节点颜色涂黑
2.3.1.3 有两个子节点
使用后继节点作为替换的删除节点,情形转至1或2处理
2.3.2 删除后的平衡
黑色无子节点的情况下引发此操作
2.3.2.1 N为根
无操作
2.3.2.2 兄黑
兄子全黑
父红,则将父涂黑,将兄涂红(父加兄减,已平衡);父黑,S涂红,将P作为新N,递归处理
左兄左子红
以P为支点右旋,交换P和S颜色(原P涂黑,S未知),SL涂黑(N加父不变,已平衡)
右兄右子红
以P为支点左旋,交换P和S颜色(P涂黑,S未知),SR涂黑(N加父不变,已平衡)
左兄右子红
以S为支点左旋,交换S和SR颜色(S涂红,SR涂黑),转为左兄左子红情况处理
右兄左子红
以S为支点右旋,交换S和SL颜色(S涂红,SL涂黑),转为右兄右子红情况处理
2.3.2.3 兄红
左兄
以P为支点进行右旋,交换P和S的颜色(S涂黑,P涂红)
右兄
以P为支点进行左旋,交换P和S的颜色(S涂黑,P涂红)
3 总结
3.1 插入
3.1.1 先插入
首先是先插入再说;插入后,以刚插入的节点作为当前平衡节点N,进行平衡操作。现在回头看插入平衡的这几种情形,其实并不复杂:
3.1.2 N为根涂黑
N为根:涂黑完事;
3.1.3 父黑不用管
啥事不用管;
3.1.4 父红叔红
父/叔涂黑,祖父涂红,然后把祖父当成新的平衡节点递归处理(我们下面平衡了,让他老人家和上面沟通吧);
3.1.5 父红叔黑:
父节点和新插入节点同一边的话,扭一下就完事了(同左右旋,同右左旋,顺便涂色);不在同一边的话,先扭到同一边吧。
3.2 删除
3.2.1 红色的叶子节点
删除动作(移除节点)之后,看看这个节点是不是黑色的叶子节点,如果不是,简单处理就可以达到平衡了;
3.2.2 黑色的叶子节点,先看N,再看兄弟颜色
先看N是不是根节点,是的话啥时不用管;不是的话看兄弟什么颜色:
3.2.2.1 兄弟是红色:进行旋转涂色,去到兄弟为黑色那里处理
3.2.2.2 兄弟是黑色,看看兄弟子节点是不是全部都是黑。
(1)全黑的话,看父什么颜色进行对应处理;
(2)不全黑,看兄在的位置,兄在左的话,看兄的左子是不是红色,进行对应处理;兄在右的话,看兄的右子是不是红色,进行对应处理。
4 代码实现,仅供参考
package com.RBTree;
/**
* Created by mqy on 2020/1/2.
*/
public class RBTest {
public static void main(String[] args) throws Exception{
MyNode a=new MyNode();
//a.setVal(5);
//MyNode a=null;
for(int i=0;i<10;i++){
insertRBTree(getRoot(a),i);
listTree(getRoot(a));
System.out.println();
}
for(int i=10;i>1;i--){
MyNode s=search(getRoot(a),i);
if(s==null){
System.out.println();
}else{
MyNode p=deleteRBTree(s);
listTree(getRoot(p));
System.out.println();
}
}
//listTree(a);
/* revolveLeft(a);
System.out.println();
listTree(a.getParent());
revolveRight(a.getParent());
System.out.println();
listTree(a);*/
}
private static MyNode search(MyNode z,int v){
if(z.getVal()==v){
return z;
}
if(z.getVal()<v){
if(z.getRight()==null){
return null;
}
return search(z.getRight(),v);
}else{
if(z.getLeft()==null){
return null;
}
return search(z.getLeft(),v);
}
}
/**
* 插入
* @param t
* @param z
*/
private static void insertRBTree(MyNode t,Integer z){
if(z==6){
System.out.println();
}
if(t==null || z==null){
return;
}
MyNode temp=t;
MyNode p=null;
while(temp!=null){
p=temp;
if(temp.getVal()==null){
t.setVal(z);
t.setColor(0);
listTree(getRoot(t));
System.out.println();
balance(t);
return;
}else{
if(z.compareTo(temp.getVal())>0){
temp=temp.getRight();
}else{
temp=temp.getLeft();
}
}
}
MyNode n=new MyNode(z);
n.setColor(0);
n.setParent(p);
if(z.compareTo(p.getVal())>0){
p.setRight(n);
}else{
p.setLeft(n);
}
listTree(getRoot(n));
System.out.println();
balance(n);
}
/**
* 平衡
* @param z
*/
private static void balance(MyNode z){
if(z.getParent()==null){
z.setColor(1);
return;
}
//父红
if(z.getParent().getColor()==0){
//父不是根节点
if(z.getParent().getParent()!=null){
//获取叔叔节点
boolean pIsL=false;
MyNode uncle=z.getParent().getParent().getLeft();
if(z.getParent()==z.getParent().getParent().getLeft()){
uncle=z.getParent().getParent().getRight();
pIsL=true;
}
//叔叔红
if(uncle!=null && uncle.getColor()==0){
//PU涂黑
z.getParent().setColor(1);
uncle.setColor(1);
//GP涂红
uncle.getParent().setColor(0);
balance(uncle.getParent());
}else{
//父是左子节点
if(pIsL){
//当前节点是左子节点
if(z==z.getParent().getLeft()){
//原P涂黑,原GP涂红
z.getParent().setColor(1);
z.getParent().getParent().setColor(0);
//以GP右旋
revolveRight(z.getParent().getParent());
}else{
//P左旋
revolveLeft(z.getParent());
balance(z.getParent());
}
}else{
//当前节点是右子节点
if(z!=z.getParent().getLeft()){
//原P涂黑,原GP涂红
z.getParent().setColor(1);
z.getParent().getParent().setColor(0);
//以GP左旋
revolveLeft(z.getParent().getParent());
}else{
//P右旋
revolveRight(z.getParent());
balance(z.getParent());
}
}
}
}
}
}
/**
* 删除
* @param z
* @param
*
*/
private static MyNode deleteRBTree(MyNode z){
if(z==null){
return null;
}
MyNode parent=z.getParent();
//将红黑树当成一个二叉查找树,将节点删除
if(z.getLeft()==null || z.getRight()==null){
}else{
if(z.getParent()==null){
z=null;
}else{
//有一个子节点,断开子节点,连接子节点和父节点
MyNode singleNode=null;
if(z.getLeft()!=null){
z.getLeft().setParent(z.getParent());
singleNode=z.getLeft();
}else if(z.getRight()!=null){
z.getRight().setParent(z.getParent());
singleNode=z.getRight();
}
//黑色无子节点则需要平衡
if(singleNode==null ){
if(z.getColor()==1){
listTree(getRoot(z));
System.out.println();
balanceAfterDel(z);
}
}else{
singleNode.setColor(1);
}
//断开父节点
if(z==z.getParent().getLeft()){
z.getParent().setLeft(singleNode);
}else{
z.getParent().setRight(singleNode);
}
z.setParent(null);
}
//两个子节点,要找后继节点替换当前节点并删除后继节点
MyNode successor=getSuccessor(z,true);
z.setVal(successor.getVal());
deleteRBTree(successor);
}
return parent;
}
/**
* 平衡
* @param z
*/
private static void balanceAfterDel(MyNode z){
if(z.getParent()!=null){
boolean bIsL=true;
MyNode brother=z.getParent().getLeft();
if(z==z.getParent().getLeft()){
brother=z.getParent().getRight();
bIsL=false;
}
//兄黑
//兄左
if(brother.getColor()==1){
if(bIsL){
//左兄左子红
if(brother.getLeft()!=null && brother.getLeft().getColor()==0 ){
int color=brother.getParent().getColor();
brother.getParent().setColor(brother.getColor());
brother.setColor(color);
brother.getLeft().setColor(1);
revolveRight(brother.getParent());
}else if(brother.getRight()!=null && brother.getRight().getColor()==0){
//左兄右子红
int color=brother.getColor();
brother.setColor(brother.getRight().getColor());
brother.getRight().setColor(color);
balanceAfterDel(z);
}else{
//兄子全黑
//兄子全黑
//父红
brother.setColor(0);
if(brother.getParent().getColor()==0){
//父红,则将父涂黑,将兄涂红(父加兄减,已平衡)
brother.getParent().setColor(1);
}else{
balanceAfterDel(brother.getParent());
}
}
}else{
//右兄右子红
if(brother.getRight()!=null && brother.getRight().getColor()==0 ){
int color=brother.getParent().getColor();
brother.getParent().setColor(brother.getColor());
brother.setColor(color);
brother.getRight().setColor(1);
revolveLeft(brother.getParent());
}else if(brother.getLeft()!=null && brother.getLeft().getColor()==0){
//右兄左子红
int color=brother.getColor();
brother.setColor(brother.getLeft().getColor());
brother.getLeft().setColor(color);
balanceAfterDel(z);
}else{
//兄子全黑
//父红
brother.setColor(0);
if(brother.getParent().getColor()==0){
//父红,则将父涂黑,将兄涂红(父加兄减,已平衡)
brother.getParent().setColor(1);
}else{
balanceAfterDel(brother.getParent());
}
}
}
}else{
//兄红
int color=brother.getParent().getColor();
brother.getParent().setColor(brother.getColor());
brother.setColor(color);
if(brother==brother.getParent().getLeft()){
revolveRight(brother.getParent());
}else{
revolveLeft(brother.getParent());
}
}
}
}
/**
* 寻找后继节点
* @param z
* @return
*/
private static MyNode getSuccessor(MyNode z,boolean first){
if(first){
if(z.getRight()!=null){
getSuccessor(z.getRight(),false);
}else{
return null;
}
}else{
if(z.getLeft()!=null){
getSuccessor(z.getLeft(),false);
}else{
return z;
}
}
return z;
}
/**
* 左旋
* @param z
*/
private static void revolveLeft(MyNode z){
if(z==null||z.getRight()==null){
return;
}
//将目标位置让给其右子节点(父节点及其父节点的左右位置)
z.getRight().setParent(z.getParent());
if(z.getParent()!=null){
if(z.getParent().getLeft()==z){
z.getParent().setLeft(z.getRight());
}else{
z.getParent().setRight(z.getRight());
}
}
//目标父节点设置为其右子节点
z.setParent(z.getRight());
//目标右子节点的左子节点的父节点变为目标节点的右子节点
if(z.getRight().getLeft()!=null){
z.getRight().getLeft().setParent(z);
}
//将目标右子节点的左子节点给目标节点作为右子节点
z.setRight(z.getRight().getLeft());
//目标节点设为其右子节点(当前父节点)的左节点
z.getParent().setLeft(z);
}
/**
* 右旋
* @param z
*/
private static void revolveRight(MyNode z){
if(z==null||z.getLeft()==null){
return;
}
//将目标位置让给其左子节点(父节点及其父节点的左右位置)
z.getLeft().setParent(z.getParent());
if(z.getParent()!=null){
if(z.getParent().getLeft()==z){
z.getParent().setLeft(z.getLeft());
}else{
z.getParent().setRight(z.getLeft());
}
}
//目标父节点设置为其左子节点
z.setParent(z.getLeft());
//目标左子节点的右子节点的父节点变为目标节点的左子节点
if(z.getLeft().getRight()!=null){
z.getLeft().getRight().setParent(z);
}
//将目标左子节点的右子节点给目标节点作为左子节点
z.setLeft(z.getLeft().getRight());
//目标节点设为其右子节点(当前父节点)的左节点
z.getParent().setRight(z);
}
/**
* 获取根节点
* @param t
* @return
*/
private static MyNode getRoot(MyNode t){
while(t!=null && t.getParent()!=null){
t=t.getParent();
}
return t;
}
/**
* 中序遍历
* @param t
*/
private static void listTree(MyNode t){
if(t!=null){
if(t.getLeft()!=null){
listTree(t.getLeft());
}
String parent="";
if(t.getParent()!=null && t.getParent().getVal()!=null){
parent=t.getParent().getVal()+"";
}
String color="红";
if(t.getColor()==1){
color="黑";
}
System.out.print(t.getVal()+"("+parent+")("+color+")");
if(t.getRight()!=null){
listTree(t.getRight());
}
}
}
public static class MyNode{
private Integer val;
private MyNode left;
private MyNode right;
private MyNode parent;
private Integer color;//0是黑,1是红
public MyNode(){}
public MyNode(Integer val){
this.val=val;
}
public Integer getVal() {
return val;
}
public void setVal(Integer val) {
this.val = val;
}
public MyNode getLeft() {
return left;
}
public void setLeft(MyNode left) {
this.left = left;
}
public MyNode getRight() {
return right;
}
public void setRight(MyNode right) {
this.right = right;
}
public MyNode getParent() {
return parent;
}
public void setParent(MyNode parent) {
this.parent = parent;
}
public Integer getColor() {
return color;
}
public void setColor(Integer color) {
this.color = color;
}
}
}