红黑树 RBT

二叉查找树 BST : https://blog.csdn.net/cj_286/article/details/90183298

二叉平衡树 AVL : https://blog.csdn.net/cj_286/article/details/90217072

红黑树 RBT : https://blog.csdn.net/cj_286/article/details/90245150

 

红黑树(Red Black Tree) 是一种自平衡二叉查找树
红黑树和AVL树类似,都是在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能。

性质

红黑树是每个节点都带有颜色属性的二叉查找树,颜色或红色或黑色。其性质如下:
性质1. 节点是红色或黑色。
性质2. 根节点是黑色。
性质3 每个叶节点(NULL节点,空节点)是黑色的。
性质4 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)
性质5. 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。
红黑树又叫RedBlackTree、RBT,而且性质5也被称为黑高、BlackHeight、BH
RBT也必定是一个BST(二叉搜索树)
任意一棵以黑色节点为根的子树也必定是一棵红黑树(与BST、AVL的递归定义类似)

首先来看一个黑高为3的一棵红黑树

rbt-1-1-1

在实际应用中可省略null节点,如图所示

rbt-1-1-2

红黑树不一定是一棵平衡的二叉搜索树,如下图是一棵黑高为2的红黑树,但是它不是一棵平衡树

rbt-1-2-1

所以得出以下结论:
1.红黑树不像AVL树一样,永远保持绝对的平衡
2.红黑树是一种相对平衡的二叉树
3.红黑树,若H(left) >= H(right),则:H(left) <= 2*H(right) + 1 ,但BH(left) == BH(right)
         若H(left) < H(right),则:H(right) < 2*H(left) + 1 ,但BH(left) == BH(right)
4.定理:N个节点的RBT,最大高度是2log(N+1) ,这个是严格证明的
5.查询效率AVL略好于RBT
6.插入删除效率RBT比AVL好一点(总的来说,插入删除查询结合,RBT要好一点)
在前两章中,已经解决了BST的插入,删除,查询(put、remove、get、getEntry、deleteEntry)
AVL的旋转,插入调整,删除调整(rotateRight、rotateLeft、fixAfterInsertion、fixAfterDeletion)
还有获取最小节点,最大节点,前趋节点,后继节点(getFirstEntry、getLastEntry、successor、predecessor)
RBT其实和AVL一样,AVL是在插入和删除时,分别调用fixAfterInsertion、fixAfterDeletion使其保持平衡,RBT也一样,在BST插入删除时,分别调用fixAfterInsertion、fixAfterDeletion使其保持满足红黑树的那五条性质即可
RBT的插入调整分三种情况:case1、case2、case3,删除调整分四种情况:case1、case2、case3、case4,解决了这些红黑树就完成了。下面分别讨论

RBT插入删除调整是自顶向下还是自底向上?
RBT与AVL类似,在调整某个节点p之前,必须先保证p的左子树left、右子树right都已经是RBT,所以这是一个由多个子问题成立来决定总问题是否成立的算法,所以RBT的插入、删除调整算法均是自底向上(bottom up)
调整算法的递归形式的伪代码

function fixRBTPostOrder(Node p){
    if(p != null){
        fixRBTPostOrder(p.left);
        fixRBTPostOrder(p.left);
        dealWith(p);
    }
}

JDK TreeMap的fixAfterInsertion源码分析

private static <K,V> boolean colorOf(Entry<K,V> p) {
        return (p == null ? BLACK : p.color);
    }

    private static <K,V> Entry<K,V> parentOf(Entry<K,V> p) {
        return (p == null ? null: p.parent);
    }

    private static <K,V> void setColor(Entry<K,V> p, boolean c) {
        if (p != null)
            p.color = c;
    }

    private static <K,V> Entry<K,V> leftOf(Entry<K,V> p) {
        return (p == null) ? null: p.left;
    }

    private static <K,V> Entry<K,V> rightOf(Entry<K,V> p) {
        return (p == null) ? null: p.right;
    }

这些函数都是做判空处理的一些简单封装

/** From CLR */
    private void fixAfterInsertion(Entry<K,V> x) {
        x.color = RED;

        while (x != null && x != root && x.parent.color == RED) {
            if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
                Entry<K,V> y = rightOf(parentOf(parentOf(x)));
                if (colorOf(y) == RED) {
                    setColor(parentOf(x), BLACK);
                    setColor(y, BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    x = parentOf(parentOf(x));//从这里可以看出该算法也是自底向上,指针不断回溯的一个过程
                } else {
                    if (x == rightOf(parentOf(x))) {
                        x = parentOf(x);
                        rotateLeft(x);
                    }
                    setColor(parentOf(x), BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    rotateRight(parentOf(parentOf(x)));
                }
            } else {
                Entry<K,V> y = leftOf(parentOf(parentOf(x)));
                if (colorOf(y) == RED) {
                    setColor(parentOf(x), BLACK);
                    setColor(y, BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    x = parentOf(parentOf(x));
                } else {
                    if (x == leftOf(parentOf(x))) {
                        x = parentOf(x);
                        rotateRight(x);
                    }
                    setColor(parentOf(x), BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    rotateLeft(parentOf(parentOf(x)));
                }
            }
        }
        root.color = BLACK;
    }

从x = parentOf(parentOf(x));可以看出,从插入的节点开始,不断回溯的过程,如果是新增,插入的节点肯定是叶子节点,所以从底开始不断的向上回溯调整RBT,如果是修改插入则无需调整

/** From CLR */
    private void fixAfterDeletion(Entry<K,V> x) {
        while (x != root && colorOf(x) == BLACK) {
            if (x == leftOf(parentOf(x))) {
                Entry<K,V> sib = rightOf(parentOf(x));

                if (colorOf(sib) == RED) {
                    setColor(sib, BLACK);
                    setColor(parentOf(x), RED);
                    rotateLeft(parentOf(x));
                    sib = rightOf(parentOf(x));
                }

                if (colorOf(leftOf(sib))  == BLACK &&
                    colorOf(rightOf(sib)) == BLACK) {
                    setColor(sib, RED);
                    x = parentOf(x);//从这里可以看出该算法也是自底向上,指针不断回溯的一个过程
                } else {
                    if (colorOf(rightOf(sib)) == BLACK) {
                        setColor(leftOf(sib), BLACK);
                        setColor(sib, RED);
                        rotateRight(sib);
                        sib = rightOf(parentOf(x));
                    }
                    setColor(sib, colorOf(parentOf(x)));
                    setColor(parentOf(x), BLACK);
                    setColor(rightOf(sib), BLACK);
                    rotateLeft(parentOf(x));
                    x = root;
                }
            } else { // symmetric
                Entry<K,V> sib = leftOf(parentOf(x));

                if (colorOf(sib) == RED) {
                    setColor(sib, BLACK);
                    setColor(parentOf(x), RED);
                    rotateRight(parentOf(x));
                    sib = leftOf(parentOf(x));
                }

                if (colorOf(rightOf(sib)) == BLACK &&
                    colorOf(leftOf(sib)) == BLACK) {
                    setColor(sib, RED);
                    x = parentOf(x);
                } else {
                    if (colorOf(leftOf(sib)) == BLACK) {
                        setColor(rightOf(sib), BLACK);
                        setColor(sib, RED);
                        rotateLeft(sib);
                        sib = leftOf(parentOf(x));
                    }
                    setColor(sib, colorOf(parentOf(x)));
                    setColor(parentOf(x), BLACK);
                    setColor(leftOf(sib), BLACK);
                    rotateRight(parentOf(x));
                    x = root;
                }
            }
        }

        setColor(x, BLACK);
    }

删除调整和插入调整一样,都是自底向上,不断回溯调整

插入

插入原则
若插入的节点为黑色,肯定违反性质5,所以只能插入红色节点,可能违反性质4,需要继续调整。

RBT的插入调整
考虑插入到左子树的情况,规定如下标记:

rbt-1-3-1

1.正在处理的节点X,也叫子节点
2.父节点P
3.爷爷节点G
4.叔叔节点Y
5.A3表示黑高为3的红黑树

插入调整算法的正确性证明
每将节点进行染色、旋转操作,我们需要考虑以下两点
1.是否会引起左右子树BH不一致,即是否满足性质5
2.有无继续破坏性质4的可能

RBT插入调整情况如下
1.无需调整的情况为:
    1).X为根节点,将X由红染黑,简称rootOver
    2).父节点P为黑色,BlackParentOver,简称bpOver
2.仅仅需要考虑父节点P为红色的情形,由于性质4,爷爷节点G必定为黑色,分为以下三种情况:
    1).case1:Y为红色,X可左可右;P、Y染黑,G染红,X回溯至G
    2).case2:Y为黑色,X为右孩子;左旋P,X指向P,转化为case3
    3).case3:Y为黑色,X为左子树;P染黑,G染红,右旋G,结束
3.结论:RBT的插入调整最多旋转2次

无需调整情况分析
1.越界 (x==null)
2.X是根节点 (x==root)
3.父节点P的颜色为黑色,必定满足性质4 (x.parent.color!=RED)
    所以while循环调整的条件语句是while(x!=null && x!=root && x.parent.color==RED)
4.不管根节点是否是黑色,将根节点root染黑

详细分析
case1
1.条件:P为G的左孩子,Y为红色,X可左可右
2.处理方式:P、Y染黑,G染红,X回溯至G
JDK TreeMap源码分析

if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {//如果X父亲P是G的左孩子
                Entry<K,V> y = rightOf(parentOf(parentOf(x)));//获取叔叔节点Y
                if (colorOf(y) == RED) {//叔叔节点Y为红色,X可左可右
                    setColor(parentOf(x), BLACK);//父节点P染黑
                    setColor(y, BLACK);//叔叔节点Y染黑
                    setColor(parentOf(parentOf(x)), RED);//祖父G染红
                    x = parentOf(parentOf(x));//指针回溯,X回溯至G
                }

case1图解如下所示

rbt-1-3-2

case1算法正确性证明
经过case1调整之后:
1.显然X、P、Y、G的关系均满足性质4
2.X的BH未改变,所以P是满足性质5的
3.P、Y的BH同时增加了1,那么G也还是满足性质5的
4.G的BH未改变,所以整个RBT是满足性质5的
5.G是红色,可能违反性质2、性质4,所以需要继续调整 (G为根节点或G的父节点为红色)

case1的转化
由于G是一个红色节点,故case1可转化为:case1;case2;case3;rootOver,把根节点由红染黑,结束调整,此时整个红黑树的BH增加1,这是唯一增加整个树BH的情形。

case2
1.条件:P为G的左孩子,Y为黑色,X为右孩子
2.处理方式:左旋P,X指向P,转化为case3
JDK TreeMap源码分析

else { //叔叔节点Y是黑色
                    if (x == rightOf(parentOf(x))) { //X是P的右孩子
                        x = parentOf(x); //X指向P
                        rotateLeft(x);//左旋P
                    }

case2图解如下所示

rbt-1-3-3

case2算法正确性证明
经过case2调整之后:
1.P左右子树的BH均未改变,所以P满足性质5
2.X左右子树的BH均未改变,所以X满足性质5
3.X、Y的BH均未改变,所以G满足性质5
4.G的BH未改变,所以可以推导出整个RBT满足性质5
5.P、X的关系仍然违反性质4,需要继续调整

case2的转化
1.case2只能转化为case3
2.case2不会引起BH增加

case3
1.条件:P为G的左孩子,Y为黑色,X为左孩子
2.处理方式:P染黑,G染红,右旋G,结束
JDK TreeMap源码分析

setColor(parentOf(x), BLACK);//父节点P染黑
setColor(parentOf(parentOf(x)), RED);//祖父节点G染黑
rotateRight(parentOf(parentOf(x)));//右旋G

case3图解如下所示

rbt-1-3-4

case3算法正确性证明
经过case3调整之后:
1.G左右子树的BH均未改变,所以G满足性质5
2.P左右子树的BH均未改变,所以P满足性质5
3.P的BH未改变,所以整个RBT满足性质5
4.P的颜色为黑色,必定满足性质2、性质4,算法结束

case3的转化
case3无需转化,也不会引起BH增加

AVL插入与RBT的插入对比
1.插入元素都是BST的插入,,区别在于插入后的调整
2.旋转次数:AVL与RBT均是O(1) (旋转次数都是常数级别)
3.指针回溯次数,最好情况:
    1).很早就遇到单旋或双旋的情况,为O(1)
    2).很早就遇到case2或case3,为O(1)
4.指针回溯次数,最坏情况:
    1).回溯至根节点才发现平衡因子大于1,为logN
    2).不断执行case1,知道根节点,但每次向上回溯两层,为logN / 2
5.插入删除效率:RBT好于AVL
6.查询效率:AVL略好于RBT

进一步细化case
1.为了方便举例和调试,进一步细化case
2.根节点结束rootOver,黑色父亲结束bpOver
3.P为G的左孩子,三个leftCase:
    1).leftCase1: Y为红,X可左可右;P、Y变黑,G变红,X变G
    2).leftCase2: Y为黑,X为右孩子;左旋P,X变P
    3).leftCase3: Y为左孩子;G为左孩子;G变红、P变黑、右旋G
4.P为G的右孩子,三个rightCase:(和3正好对称处理)
    1).rightCase1: Y为红,X可左可右;P、Y变黑,G变红,X变G
    2).rightCase2: Y为黑,X为左孩子;右旋P,X变P
    3).rightCase3: Y为黑,X为右孩子;G变红,P变黑,左旋G

插入演示
依次插入[12,1,9,2,0,11,7,19,4,15,18,5,|14,13,10,16,6,3,8,17]
插入12

rbt-1-4-1

12是根节点,将其染黑,结束
插入1

rbt-1-4-2

父节点为黑色,直接插入,bpOver
插入9

rbt-1-4-3

父节点1是红节点,9是1的右孩子,属于leftCase2,所以将1进行左旋,转成leftCase3,再将12进行右旋,9染成黑色,12染成红色
插入2

rbt-1-4-4

父节点1和叔叔节点12都为红色,属于leftCase1,将1、12染黑,9染红,指针回溯至9,9是根节点,9染成黑色,rootOver,BH增加了1
插入0

rbt-1-4-5

父节点1为黑色,bpOver
插入11

rbt-1-4-6

父节点12为黑色,bpOver
插入7

rbt-1-4-7

父节点2和叔叔节点0都为红色,属于rightCase1,将2、0染黑,1染红,指针回溯至1,1的父节点9是黑色的,bpOver
插入19

rbt-1-4-8

父节点12为黑色,bpOver
插入4

rbt-1-4-9

父节点7是祖父节点2的右节点,且叔叔节点(nulll)是黑节点,属于rightCase2,所以将7进行右旋,转成rightCase3,继续调整,将节点2左旋,将节点4染黑,节点2染红,插入完成
插入15

rbt-1-4-10

父节点19和叔叔节点11都是红色节点,属于rightCase1,将19和11染黑,祖父节点12染红,指针回溯至12,12的父节点9是黑色,bpOver
插入18

rbt-1-4-11

18是右孩子,父节点15是祖父节点的左孩子,叔叔节点(null)是黑色节点,属于leftCase2,将15左旋,转成leftCase3,继续调整,将爷爷节点19右旋,将19染红,18染黑,插入完成
插入5

rbt-1-4-12

5是左孩子,父节点7是祖父节点的右节点,父节7和叔叔节点2都是红色,属于rightCase1,将7和2染黑,祖父节点4染红,指针回溯至4,继续调整,4的父节点和叔叔节点12都是红色的,4的父节点1属于祖父节点9的左孩子,属于leftCase1,将1和12都染黑,将(根)节点9染红,发现9是根节点,再将9染黑,rootOver,BH增加1,插入结束。

删除

RBT的删除原则
1.删除红色节点,不会影响BH,也不会违反性质4,,无需调整
2.删除黑色节点,节点所在子树的BH减1,需要调整

RBT的删除调整
考虑删除左子树的情况,规定如下标记:
1.正在处理的节点为X
2.父节点为P
3.兄弟节点为S (sib)
4.左侄子(兄弟节点的左孩子)为LN
5.右侄子(兄弟节点的右孩子)为RN

rbt-1-5-1


删除调整算法的正确证明
1.每将节点进行染色、旋转操作,都需要考虑:
    1).是否违反性质5,如:
            X的BH只能不变或增加,否则X的BH将比S的更小
    2).是否违反性质4,如果违反,染黑还是继续回溯

RBT的删除调整 (删除的节点是替换的叶子节点)
1.需要删除的节点X为红色,直接删除X 
2.其他无需调整的情况为:
    1).当前X为根节点,无论root是什么颜色,都将root染黑,简称rootOver
    2).当前X为红色,将X染黑,结束,简称redOver
3.删除左孩子X,分为四种情况:
    case1: S为红色;S染黑,P染红,左旋P
    case2: S为黑色,黑LN,黑RN;S染红,X回溯至P
    case3: S为黑色,红LN,黑RN;LN染黑,S染红,右旋S;转成case4
    case4: 黑S,LN随意,红RN;S变P的颜色,P和RN染黑,左旋P
4.删除右孩子X,情况正好和删除左孩子X对称

详细分析
无需调整1
1.删除的X本身就是红色节点,直接删除
JDK TreeMap源码分析 

// Fix replacement
            if (p.color == BLACK)
                fixAfterDeletion(replacement);
        } else if (p.parent == null) { // return if we are the only node.
            root = null;
        } else { //  No children. Use self as phantom replacement and unlink.
            if (p.color == BLACK)
                fixAfterDeletion(p);

只有在删除节点是黑色的请款才需要调整

无需调整2
1.回溯指针时遇到的情形:
    1).当前X为根节点,无论root是什么颜色,都将root染黑,将根节点染黑的同时满足性质2、4、5,简称rootOver
    2).当前X为红色,将X染黑,结束,简称redOver
       (在执行这一步之前,BH(left)==BH(right)-1,执行完这一步,正好使得BH(left)==BH(right),删除调整算法结束)
JDK TreeMap源码分析

while (x != root && colorOf(x) == BLACK) {
            if (x == leftOf(parentOf(x))) {
...
}
setColor(x, BLACK);

需要调整的情况  
case1
1.条件:S为红色
2.隐含条件:由于性质4,P、LN、RN必定都为黑色
处理方式:S染黑,P染红,旋转P,LN成为新的S(sib)
JDK TreeMap源码分析

if (x == leftOf(parentOf(x))) { //X是P的左孩子
                Entry<K,V> sib = rightOf(parentOf(x));//获取兄弟节点S

                if (colorOf(sib) == RED) {//S为红色
                    setColor(sib, BLACK);//S染黑
                    setColor(parentOf(x), RED);//P染红
                    rotateLeft(parentOf(x));//左旋P
                    sib = rightOf(parentOf(x));//原来的LN成为新的S
                }

case1图解如下所示

rbt-1-5-2

case1的正确性证明
经过case调整之后:
1.符合性质4
2.BH(X)比BH(LN)少1,则P违反了性质5
3.P违反性质5,则S也违反了性质5
4.需要继续调整X

case1的转化情况
1.case1可转化为:
    1).case2-2
    2).case3
    3).case4-1、case4-2
2.case1不会引起BH的变化

case2
1.case2-1条件:S、LN、RN均为黑色,P为黑色
2.case2-2条件:S、LN、RN均为黑色,P为红色
3.处理方式相同:S染红,X回溯至P
JDK TreeMap源码分析

if (colorOf(leftOf(sib))  == BLACK &&
                    colorOf(rightOf(sib)) == BLACK) { //S、LN、RN均为黑色
                    setColor(sib, RED); //将S染红
                    x = parentOf(x); //X回溯至P
                } 

case2-1图解如下所示

rbt-1-6-1

case2-1的正确性证明
经过case2-1调整之后:
1.符合性质4
2.S的BH减少了1,BH(X)==BH(S),则P符合性质5
3.P的BH减少了1,则整个RBT违反性质5
4.需要继续调整

case2-1的转化情况
1.由于P是黑色,故case2-1可转化为任意case:
    1).case1
    2).case2-1、case2-2
    3).case3
    4).case4-1、case4-2
2.若P为根节点,则执行case2-1会引起BH的减小,这是唯一减小红黑树BH的情形

case2-2图解如下所示

rbt-1-6-2

case2-2的正确性证明
1.经过case2-2调整之后
    1).BH(S)减少了1,BH(X) == BH(S),则P符合性质5
    2).P的BH减少了1,则整个RBT违反了性质5
    3).P与S的关系违反了性质4 
2.调整策略
    1).redOver
    2).直接将P染黑,则BH(P)加1,所以满足性质4且RBT平衡

case2-2的转化情况
1.case2-2只能转化为redOver,并且结束调整

rbt-1-6-3

 

case3
1.条件:S为黑色,LN为红色,RN为黑色
2.处理方式:LN染黑,S染红,右旋S,S指向LN,转化为case4-1或者case4-2
JDK TreeMap源码分析

if (colorOf(rightOf(sib)) == BLACK) { //S为黑色,LN为红色,RN为黑色
                        setColor(leftOf(sib), BLACK); //LN染黑
                        setColor(sib, RED); //S染红
                        rotateRight(sib); //右旋S
                        sib = rightOf(parentOf(x)); //S指向LN
                    }

case3图解如下所示

rbt-1-7-1

case3的正确性证明
经过case3调整之后
1.S的左右子树BH相等,则S符合性质5
2.LN的左右子树BH相等,则LN符合性质5
3.X的BH仍然比LN的BH少1,所以违反性质5
4.需要继续调整X

case3的转化情况
1.case3可转化为case4-1、case4-2
2.case3不会引起BH的变化

case4
1.条件:S为黑色,P可红可黑,RN为黑色
    1).case4-1: LN为红色
    2).case4-2: LN为黑色
2.处理方式相同:S的颜色设置为与P相同,P染黑,RN染黑,左旋P,X指向根节点,rootOver
JDK TreeMap源码分析

//S为黑色,P可红可黑,RN为黑色,LN可红可黑
setColor(sib, colorOf(parentOf(x))); //S的颜色设置为与P相同
                    setColor(parentOf(x), BLACK); //P染黑
                    setColor(rightOf(sib), BLACK);  //RN染黑
                    rotateLeft(parentOf(x)); //左旋P
                    x = root; //X回溯至根节点

case4-1图解如下所示

rbt-1-8-1

case4-2图解如下所示

rbt-1-8-2

case4的正确性证明
经过case4调整之后:
1.染黑之后的P正好填补了左子树缺少的一个BH
2.RN染黑,正好填补了空缺的黑S,右子树的BH不变
3.BH(P)==BH(RN),则符合性质5
4.以S为根的子树BH和删除前一样,所以整个RBT平衡
5.没有任何违反性质4的节点
6.rootOver

case4的转化情况
1.rootOver,无需转化

转化情况与旋转次数
1.完整的case转化情况
    1).case1可以转化为:case2-2、case3、case4
    2).case2-1可以转化为:case1、case2-1、case2-2、case3、case4
    3).case2-2不可转化
    4).case3可以转化为:case4
    5).case4不可转化
2.RBT的删除调整最多旋转3次 (如:case1 -> case3 -> case4)

AVL的删除和RBT的删除对比
1.删除节点都是BST的删除,主要区别在于调整
2.旋转次数:AVL与RBT均是常数级别,所以是O(1)
3.指针回溯次数,最好情况:
    1).AVL:类似插入,可通过优化提前结束递归,为O(1)
    2).RBT:很早就遇到case1、case2-2、case3或case4,为o(1)
4.指针回溯次数,最坏情况
    1).AVL:回溯至根节点才发现平衡因子大于1,为logN
    2).RBT:不断执行case2-1,知道根节点,为logN;但是,RBT大部分形态下是红黑相间的,一直遇不到红色节点的情况很少见
5.删除效率:RBT略微好于AVL

进一步细化删除算法的case
左孩子
1.leftCase1:S为红色;S染黑,P染红,左旋P
2.leftCase2-1:S为黑色,黑LN,黑RN,黑P;S染红,X回溯至P
3.leftCase2-2:S为黑色,黑LN,黑RN,红P;S染红,X回溯至P
4.leftCase3:S为黑色,红LN,黑RN;LN染黑,S染红,右旋S
5.leftCase4-1:黑S,红LN,红RN;S以父为名(S变P的颜色),P和RN染黑,左旋P
6.leftCase4-2:黑S,黑LN,红RN;S以父为名(S变P的颜色),P和RN染黑,左旋P
右孩子
1.rightCase1:S为红色;S染黑,P染红,右旋P
2.rightCase2-1:S为黑色,黑LN,黑RN,黑P;S染红,X回溯至P
3.rightCase2-2:S为黑色,黑LN,黑RN,红P;S染红,X回溯至P
4.rightCase3:S为黑色,红RN,黑LN;RN染黑,S染红,左旋S
5.rightCase4-1:黑S,红LN,红RN;S以父为名(S变P的颜色),P和LN染黑,右旋P
6.rightCase4-2:黑S,红LN,黑RN;S以父为名(S变P的颜色),P和LN染黑,右旋P

二叉树删除节点找替代节点有3中情况:
1.若删除节点无子节点,直接删除
2.若删除节点只有一个子节点,用子节点替换删除节点
3.若删除节点有两个孩子,用后继节点(或前趋节点)替换删除节点
删除节点被替代后,对于树来说,可以认为删除的是替代节点,3中二叉树的删除情景可以互相转换并最终都是转换为情况1、情况2

删除源码的注意点
1.需要successor,对应BST删除的情况3

// If strictly internal, copy successor's element to p and then make p
        // point to successor.
        if (p.left != null && p.right != null) {
            Entry<K,V> s = successor(p);
            p.key = s.key;
            p.value = s.value;
            p = s;
        } // p has 2 children

2.经过successor之后,情况3转化为情况1或情况2
是否需要successor
1.无需successor,直接删除(调整)节点P本身

rbt-1-9-1

2.用successor替代P,再进行删除

rbt-1-9-2

 

BST删除的情况1和情况2的执行顺序略有不同
情况1.先调整P,再删除P

else { //  No children. Use self as phantom replacement and unlink.
           //情况1:P是叶子节点,直接删除
            if (p.color == BLACK)
                fixAfterDeletion(p);//先调整
            //再进行删除(设置为null)
            if (p.parent != null) {
                if (p == p.parent.left)
                    p.parent.left = null;
                else if (p == p.parent.right)
                    p.parent.right = null;
                p.parent = null;
            }
        }

情况2.先删除P,再调整replacement

// Start fixup at replacement node, if it exists.
        Entry<K,V> replacement = (p.left != null ? p.left : p.right);
        //情况2:P只有一个孩子,replacement是孩子之一
        if (replacement != null) {
            // Link replacement to parent
            replacement.parent = p.parent;
            if (p.parent == null)
                root = replacement;
            else if (p == p.parent.left)
                p.parent.left  = replacement;
            else
                p.parent.right = replacement;

            // Null out links so they are OK to use by fixAfterDeletion.
            //先删除(设置为null)
            p.left = p.right = p.parent = null;

            // Fix replacement
            if (p.color == BLACK)
                fixAfterDeletion(replacement);//再调整
        } 

BST删除的情况1
先调整,后删除

rbt-1-9-3

 

BST删除的情况2
先删除,后调整

rbt-1-9-4

 

删除演示 
根据JDK TreeMap源码分析
依次插入[12,1,9,2,0,11,7,19,4,15,18,5,14,13,10,16,6,3,8,17]后生成的红黑树
依次删除[12,1,9,2,0,11,7,| 19,4,15,18,5,14,13,10,16 |,6,3,8,17]
依次删除[12,1,9,2,0,11,7]之后的红黑树

rbt-1-10-1


删除19
删除19是BST的删除情况1,先调整,后删除
fix(p) -> rightCase4-1 -> 18染黑、16染红、15染黑,右旋18 -> 设置p=null

rbt-1-10-2

删除4
删除4是BST的删除情况3,先找到后继节点successor=5 

rbt-1-10-3

赋值之后,指针指向了后继节点5,这时是BST的删除情况1,先调整,后删除;fix(p) -> leftCase2-2

rbt-1-10-4

8染红,节点x向上回溯至6,6为红色 -> redOver,6染黑 -> 设置p=null ,删除节点5 (图中的x为调整函数过程中的当前节点,p为传入调整函数时的节点,即p使调整之前的节点5)

rbt-1-10-5

删除15
删除的15属于叶子节点,所以属于BST删除的删除情况1,先调整,后删除
fix(p) -> leftCase3 ->18染红、17染黑、右旋18

rbt-1-10-6

这时就转化成了leftCase4-2 -> 16染黑、17染红、18染黑、左旋16 -> 设置p=null ,删除15

rbt-1-10-7

删除18
删除18属于BST的删除情况1,先调整,后删除
fix(p) -> rightCase2-2 -> 16染红、指针回溯至17 -> 17为红色 , redOver -> 17染黑 -> 设置p=null ,删除节点18

rbt-1-10-8

删除5
删除5属于BST的删除情况3,先找到后继节点successor=6 ,用6替代5,然后指针P指向后继节点6

rbt-1-10-9

这时属于BST的删除情况2 -> 6有一个右孩子,所以将6删掉,右孩子8顶替上来,replacement = 8 -> 设置p=null -> fix(rep) -> 8为红色,redOver,8染黑

rbt-1-10-10

删除14
14即右左孩子又有右孩子,数据BST的情况3,先找到后继节点successor=16 ,用16替代14 ,P指针指向16

rbt-1-10-11

这时属于BST的删除情况1,并且后继节点16属于红色节点,所以直接删除16 ->  设置p=null (这时的二叉树正好是满二叉树,并且都是黑色节点)

rbt-1-10-12

删除13
删除13属于BST的删除情况1,先调整,后删除
fix(p) -> leftCase2-1 -> 17染红、指针回溯至16 -> 6、3、8、10都是黑色,则属于rightCase2-1 -> 6染红,指针回溯至根节点10 -> rootOver , 10染黑(本来就是黑色) ->设置p=nul ,删除13 (删除13执行了两次2-1,且10为根节点,所以删除13会使整个BH减少1)

rbt-1-10-13

删除10
10既有左孩子又有右孩子,属于BST的删除情况3,先找到后继节点successor=16,用16替代10,然后指针P指向后继节点16

rbt-1-10-14

这时属于BST的删除情况2,16有一个右孩子,所以将16删掉,右孩子17顶替上来,replacement = 17 -> 设置p=null -> fix(rep) -> 17为红色,redOver,17染黑 (删除函数中待调整的节点为rep,删除调整函数中当前调整节点为x)

rbt-1-10-15

删除16
16既有左孩子又有右孩子,属于BST的删除情况3,先找到后继节点successor=17,用17替代16,然后指针P指向后继节点17

rbt-1-10-16

fix(p) -> 17的兄弟节点为红色,属于rightCase1 -> 6染黑、根节点17染红、右旋根节点17

rbt-1-10-17

x指向了17,兄弟节点8为黑色,8的两个孩子也是黑色(NULL),父节点17为红色,属于rightCase2-2 -> 8染红,指针回溯至17 -> 17为红色,redOver,17(x指针,x在删除调整函数fixAfterDeletion中)染黑  -> 设置p=nul ,删除17(p指针,p在删除函数deleteEntry中)

rbt-1-10-18

 

源码


import java.util.*;

public class RBTMap<K,V> implements Iterable<RBTMap.RBNode<K,V>>{
    private int size = 0;
    private RBNode<K,V> root;//根节点

    private Comparator<? super K> comparator;

    public RBTMap(Comparator<K> comparator) {
        this.comparator = comparator;
    }

    public RBTMap() {
    }

    public int size() {
        return this.size;
    }

    public boolean isEmpty() {
        return this.size == 0;
    }

    public int compare(Object a, Object b) {
        if (comparator != null) {
            return comparator.compare((K)a,(K)b);//JDK中也是强转的
        }else{
            Comparable<K> t = (Comparable<K>) a;
            return t.compareTo((K)b);
        }
    }

    private RBNode<K,V> parentOf(RBNode<K,V> p) {
        return p!=null ? p.parent : null;
    }

    private RBNode<K,V> leftOf(RBNode<K,V> p){
        return p != null ? p.left : null;
    }

    private RBNode<K,V> rightOf(RBNode<K,V> p){
        return p != null ? p.right : null;
    }

    private boolean isRed(RBNode<K,V> p) {
        return ((p!=null)&&(p.color==RED)) ? true : false;
    }

    private boolean isBlack(RBNode<K,V> p) {
        return !isRed(p);
    }

    private boolean colorOf(RBNode<K,V> p) {
        return (p == null ? BLACK : p.color);
    }

    private void setBlack(RBNode<K,V> p) {
        if (p!=null)
            p.color = BLACK;
    }

    private void setRed(RBNode<K,V> p) {
        if (p!=null)
            p.color = RED;
    }

    private void setColor(RBNode<K,V> p,boolean color){
        if (p != null) {
            p.color = color;
        }
    }

    /*************对红黑树节点p进行左旋操作 ******************/
    /*
     * 左旋示意图:对节点p进行左旋
     *
     *    p.p                     p.p
     *    /                       /
     *   p                       r
     *  / \                     / \
     * l   r      ----->       p  r.r
     *    / \                 / \
     *   r.l r.r             l  r.l
     *
     * 左旋做了三件事:
     * 1. 将r的左子节点赋给p的右子节点,并将p赋给r左子节点的父节点(r左子节点非空时)
     * 2. 将p的父节点p.p(非空时)赋给r的父节点,同时更新p.p的子节点为r(左或右)
     * 3. 将r的左子节点设为p,将p的父节点设为r
     */
    private void rotateLeft(RBNode<K,V> p) {
        if (p == null) return;
        //1. 将r的左子节点赋给p的右子节点,并将p赋给r左子节点的父节点(r左子节点非空时)
        RBNode<K,V> r = p.right;
        p.right = r.left;
        if (r.left != null) {
            r.left.parent = p;
        }
        //2. 将p的父节点p.p(非空时)赋给r的父节点,同时更新p.p的子节点为r(左或右)
        r.parent = p.parent;
        if (p.parent == null) {
            this.root = r;//如果p的父节点为空(即p为根节点),则将r设为根节点
        }else {
            if (p == p.parent.left) {//如果p是左子节点
                p.parent.left = r;//则也将r设为左子节点
            }else{
                p.parent.right = r;//否则将r设为右子节点
            }
        }
        // 3. 将r的左子节点设为p,将p的父节点设为r
        r.left = p;
        p.parent = r;
    }

    /*************对红黑树节点p进行右旋操作 ******************/
    /*
     * 右旋示意图:对节点p进行右旋
     *
     *       p.p                 p.p
     *       /                   /
     *      p                   l
     *     / \                 / \
     *    l   r   ----->     l.l  p
     *   / \                     / \
     * l.l l.r                  l.r r
     *
     * 右旋做了三件事:
     * 1. 将l的右子节点赋给p的左子节点,并将p赋给l右子节点的父节点(l右子节点非空时)
     * 2. 将p的父节点p.p(非空时)赋给l的父节点,同时更新p.p的子节点为l(左或右)
     * 3. 将l的右子节点设为p,将p的父节点设为l
     */
    private void rotateRight(RBNode<K,V> p) {
        if(p == null) return;
        //1. 将l的右子节点赋给p的左子节点,并将p赋给l右子节点的父节点(l右子节点非空时)
        RBNode<K,V> l = p.left;
        p.left = l.right;
        if (l.right != null) {
            l.right.parent = p;
        }
        //2. 将p的父节点p.p(非空时)赋给l的父节点,同时更新p.p的子节点为l(左或右)
        l.parent = p.parent;
        if (p.parent == null) {
            this.root = l;//如果p的父节点为空(即p为根节点),则旋转后将l设为根节点
        }else{
            if (p == p.parent.right) {//如果p是右子节点
                p.parent.right = l;//否则将l设置为右子节点
            }else{
                p.parent.left = l;//则将l也设置为左子节点
            }
        }
        //3. 将l的右子节点设为p,将p的父节点设为l
        l.right = p;
        p.parent = l;
    }

    public V put(K key,V value) {
        RBNode<K,V> e = null;
        if (root == null) {
            root = new RBNode<K,V>(key,value,BLACK);
            e = root;
            size ++;
        }else{
            RBNode<K,V> p = root;
            while (p != null) {
                int cmp = compare(key,p.key);
                if (cmp < 0) {
                    if (p.left == null) {
                        p.left = new RBNode<K,V>(key,value,BLACK,p);
                        e = p.left;
                        size ++;
                        break;
                    }else{
                        p = p.left;//再次循环比较
                    }
                } else if (cmp > 0) {
                    if (p.right == null) {
                        p.right = new RBNode<K,V>(key,value,BLACK,p);
                        e = p.right;
                        size ++;
                        break;
                    }else{
                        p = p.right;
                    }
                }else{
                    p.setValue(value);//替换旧值
                    e = p;
                    break;
                }
            }
        }
        fixAfterInsertion(e);
        //不管是插入的是新值还是重复值,都返回插入的值,这个和JDK TreeMap不一样
        return value;
    }

    /**
     * 插入调整
     * 自底向上
     * @param x
     */
    private void fixAfterInsertion(RBNode<K,V> x) {
        setRed(x);
        RBNode<K,V> parent;//定义父节点
        //需要修正的条件:父节点存在,且父节点的颜色是红色
        while ((parent = parentOf(x))!=null && isRed(parent)){
            //若父节点是祖父节点的左子节点,下面是else相反
            if (parent == leftOf(parentOf(parent))) {
                RBNode<K,V> uncle = rightOf(parentOf(parent));//获得叔叔节点
                //case1:叔叔节点也是红色
                if (uncle != null && isRed(uncle)) {
                    setBlack(parent);//把父节点和叔叔节点涂黑
                    setBlack(uncle);
                    setRed(parentOf(parent));//把祖父节点涂红
                    x = parentOf(parent);//把位置放到祖父节点处
                    continue;
                }
                //case2:叔叔节点是黑色,且当前节点是右子节点
                if (parent.right == x) {
                    x = parent;
                    rotateLeft(x);
                }
                //case3:叔叔节点是黑色,且当前节点是左子节点
                setBlack(parent);
                setRed(parentOf(parent));
                rotateRight(parentOf(parent));
            }else {//若父节点是祖父节点的右子节点,与上面的情况完全相反,本质是一样的
                RBNode<K,V> uncle = leftOf(parentOf(parent));
                //case1:叔叔节点也是红色的
                if (uncle != null && isRed(uncle)) {
                    setBlack(parent);
                    setBlack(uncle);
                    setRed(parentOf(parent));
                    x = parentOf(parent);
                    continue;
                }
                //case2:叔叔节点是黑色的,且当前节点是左子节点
                if (x == parent.left) {
                    x = parent;
                    rotateRight(x);
                }
                //case3:叔叔节点是黑色的,且当前节点是右子节点
                setBlack(parent);
                setRed(parentOf(parent));
                rotateLeft(parentOf(parent));
            }
        }
        setBlack(root);
    }

    private RBNode<K,V> getEntry(Object key) {
        RBNode<K,V> p = root;//初始化指针p,指向根节点
        while (p != null) {
            int cmp = compare(key, p.key);//比较key与p.key的大小
            if (cmp < 0) {
                p = p.left;//key小于p.key,递归(循环)查找左子树
            } else if (cmp > 0) {
                p = p.right;//key大于p.key,递归(循环)查找右子树
            } else {
                return p;//key等于p.key,查找成功
            }
        }
        return null;//查找失败
    }

    public V get(Object key) {
        RBNode<K, V> entry = getEntry(key);
        return entry != null ? entry.value : null;
    }

    /**
     * 找到最小结点(中序遍历的第一个结点)
     * @return
     */
    public RBNode<K, V> getFirstEntry() {
        return getFirstEntry(root);
    }

    /**
     * 找到以p为根节点的最小结点(中序遍历的第一个结点)
     * @param p
     * @return
     */
    private RBNode<K, V> getFirstEntry(RBNode<K, V> p) {
        if(p == null)
            return null;
        while (p.left != null)//一直往左找,直到p没有左子树
            p = p.left;
        return p;//p是最左边且第一个没有左子树的结点
    }

    /**
     * 找到最大结点(中序遍历的最后一个结点)
     * @return
     */
    public RBNode<K,V> getLastEntry() {
        return getLastEntry(root);
    }

    /**
     * 找到以p为根节点的最大结点
     * @param p
     * @return
     */
    private RBNode<K, V> getLastEntry(RBNode<K, V> p) {
        if (p == null)
            return null;
        while (p.right != null)//一直往右找,直到p没有右子树
            p = p.right;
        return p;//p是最右边且第一个没有右子树的结点
    }


    /**
     * 删除
     * @param key
     * @return
     */
    public V remove(K key) {
        RBNode<K, V> entry = getEntry(key);//查找是否有该结点
        if (entry == null) {
            return null;
        }
        V oldValue = entry.getValue();//获取value
        deleteEntry(entry);
        return oldValue;
    }

    /**
     * 删除结点p
     *
     * 二叉树删除节点找替代节点有3中情况
     * 情况1.若删除节点无子节点,直接删除
     * 情况2.若删除节点只有一个子节点,用子节点替换删除节点
     * 情况3.若删除节点有两个孩子,用后继节点(或前趋节点)替换删除节点
     * 删除节点被替代后,对于树来说,可以认为删除的是替代节点,3中二叉树的删除情景可以互相转换并最终都是转换为情况1、情况2
     *
     * BST删除的情况1和情况2的执行顺序略有不同
     * 情况1.先调整P,再删除P
     * 情况2.先删除P,再调整rep
     *
     * RBT的删除调整
     * 1.需要删除的节点X为红色,直接删除X 
     * 2.其他无需调整的情况为:
     *     1).当前X为根节点,无论root是什么颜色,都将root染黑,简称rootOver
     *     2).当前X为红色,将X染黑,结束,简称redOver
     * 3.删除左孩子X,分为四种情况:
     *     case1: S为红色;S染黑,P染红,左旋P
     *     case2: S为黑色,黑LN,黑RN;S染红,X回溯至P
     *     case3: S为黑色,红LN,黑RN;LN染黑,S染红,右旋S;转成case4
     *     case4: 黑S,LN随意,红RN;S变P的颜色,P和RN染黑,左旋P
     * 4.删除右孩子X,情况正好和删除左孩子X对称
     * @param p 要删除的结点
     */
    private void deleteEntry(RBNode<K,V> p){
        size --;
        //情况3 删除节点被替代后,对于树来说,可以认为删除的是替代节点,3中二叉树的删除情景可以互相转换并最终都是转换为情况1、情况2,所以先执行情况3
        if (p.left != null && p.right != null) { //情况3
            为了达到平衡效果,随机执行以下两种情况
            if ((size & 1) == 0) {//找右子树中最小的来替换
                RBNode<K, V> rightMin = getFirstEntry(p.right);//也可以使用获取p的后继节点方法(successor(p))
                p.key = rightMin.key;
                p.value = rightMin.value;
                p = rightMin;
            }else {//找左子树中最大的来替代
                RBNode<K, V> leftMax = getLastEntry(p.left);//也可以使用获取p的前趋节点方法(predecessor(p))
                p.key = leftMax.key;
                p.value = leftMax.value;
                p = leftMax;
            }
        }
        //以下顺序不能变(重要)
        //1.p结点只有一个孩子
        //  如果整棵树只有两个结点的时候,这是删除的是根结点,如果先执行2和3的话就会直接将root置为null
        //2.p为根结点
        //  如果整棵树只有一个结点的时候,这是删除的是跟结点,如果先执行3的话,因为这时根结点也是叶子结点,就根本不会执行2了,也就不会将root置为null了,导致根结点永远删除不了
        //3.p为叶子结点
        if (p.left == null && p.right != null) {//情况2
            RBNode<K, V> rep = p.right;
            deleteOneEntry(p,rep);
        } else if (p.left != null && p.right == null) {//情况2
            RBNode<K, V> rep = p.left;
            deleteOneEntry(p,rep);
        } else if (p.parent == null) { //如果是根结点直接将root置null
            root = null;
        } else if (p.left == null && p.right == null) {//情况1
            //情况1.先调整P,再删除P
            if (isBlack(p)) {//删除的是黑色结点才需要调整
                fixAfterDeletion(p);
            }
            //释放p
            if (p.parent != null) {
                if (p == p.parent.left) {//左孩子
                    p.parent.left = null;
                } else if (p == p.parent.right) {//右孩子
                    p.parent.right = null;
                }
                p.parent = null;//断开与父节点的连接
            }
        }
    }

    /**
     * 情况二删除
     * 若删除节点只有一个子节点,用子节点替换删除节点
     * @param p
     * @param rep
     */
    private void deleteOneEntry(RBNode<K, V> p,RBNode<K, V> rep){
        //情况2.先删除P,再调整rep
        rep.parent = p.parent;
        if (p.parent == null) { //删除的p是根结点,直接将p.right设置成root
            root = rep;
        } else if (p == p.parent.left) { //p为左孩子
            p.parent.left = rep;
        } else { //p为右孩子
            p.parent.right = rep;
        }
        //释放p
        p.left = p.right = p.parent = null;
        //调整
        if (isBlack(p)) {//删除的是黑色结点才需要调整
            fixAfterDeletion(rep);
        }
    }

    /**
     * 删除调整
     * @param x
     */
    private void fixAfterDeletion(RBNode<K,V> x){
        while (x != root && isBlack(x)){
            if (x == leftOf(parentOf(x))) { //删除左孩子X ,leftCase
                RBNode<K, V> sib = rightOf(parentOf(x));
                //case1: S为红色;S染黑,P染红,左旋P,LN成为新的S(sib)
                if (isRed(sib)) {
                    setBlack(sib);
                    setRed(parentOf(x));
                    rotateLeft(parentOf(x));
                    sib = rightOf(parentOf(x));//叔叔结点sib是相对于x结点来说的
                }
                //case2: S为黑色,黑LN,黑RN;S染红,X回溯至P
                if (isBlack(leftOf(sib)) && isBlack(rightOf(sib))) { //这里S肯定为黑色
                    setRed(sib);
                    x = parentOf(x);
                }
                //case3: S为黑色,红LN,黑RN;LN染黑,S染红,右旋S,S指向LN ;转成case4
                else { //这里S肯定为黑色
                    if (isBlack(rightOf(sib))) { //这里LN肯定为红色
                        setBlack(leftOf(sib));
                        setRed(sib);
                        rotateRight(sib);
                        //叔叔结点sib是相对于x结点来说的
                        sib = rightOf(parentOf(x));//旋转后,LN转到了S的位置,所以S需要执行LN的位置
                    }
                    //case4: 黑S,LN随意,红RN;S变P的颜色,P和RN染黑,左旋P,X指向根节点,rootOver
                    //这里的S肯定为黑色,RN肯定为红色(case3右旋S后,sib变为新的RN,case3已经将sib染成了红色)
                    setColor(sib,colorOf(parentOf(x)));
                    setBlack(parentOf(x));
                    setBlack(rightOf(sib));
                    rotateLeft(parentOf(x));
                    x = root;
                }

            }else{ 删除右孩子X ,rightCase
                RBNode<K, V> sib = leftOf(parentOf(x));
                //case1:S为红色;S染黑,P染红,右旋P,RN成为新的S(sib)
                if (isRed(sib)) {
                    setBlack(sib);
                    setRed(parentOf(x));
                    rotateRight(parentOf(x));
                    sib = leftOf(parentOf(x));
                }
                //case2:S为黑色,黑LN,黑RN;S染红,X回溯至P
                if (isBlack(rightOf(sib)) && isBlack(leftOf(sib))) { //S肯定为黑色
                    setRed(sib);
                    x = parentOf(x);
                }
                //case3:S为黑色,红RN,黑LN;RN染黑,S染红,左旋S,S指向RN ;转成case4
                else{
                    if (isBlack(leftOf(sib))) {//如果LN为黑色,RN肯定为红色
                        setBlack(rightOf(sib));
                        setRed(sib);
                        rotateLeft(sib);
                        sib = leftOf(parentOf(x));
                    }
                    //case4-1:黑S,红LN,RN随意;S以父为名(S变P的颜色),P和LN染黑,右旋P,X指向根节点,rootOver
                    //这里的S肯定为黑色,LN肯定为红色(case3左旋S后,sib变为新的LN,case3已经将sib染成了红色)
                    setColor(sib, colorOf(parentOf(x)));
                    setBlack(parentOf(x));
                    setBlack(leftOf(sib));
                    rotateRight(parentOf(x));
                    x = root;
                }
            }
        }
        setBlack(x);
    }

    //*********************************************************

    private static final boolean RED = false;
    private static final boolean BLACK = true;

    public static class RBNode<K,V>{
        boolean color;//颜色
        K key;//关键字
        V value;//value去掉就是TreeSet
        RBNode<K,V> left; //左孩子
        RBNode<K,V> right;//右孩子
        RBNode<K,V> parent;//父节点

        public RBNode(K key,V value, boolean color, RBNode<K,V> parent, RBNode<K,V> left, RBNode<K,V> right){
            this.key = key;
            this.value = value;
            this.color = color;
            this.parent = parent;
            this.left = left;
            this.right = right;
        }

        public RBNode(K key, V value,boolean color) {
            this.color = color;
            this.key = key;
            this.value = value;
        }

        public RBNode(K key, V value,boolean color, RBNode<K, V> parent) {
            this.color = color;
            this.key = key;
            this.value = value;
            this.parent = parent;
        }

        public boolean isColor() {
            return color;
        }

        public K getKey() {
            return key;
        }

        public V getValue() {
            return value;
        }

        public void setValue(V value) {
            this.value = value;
        }

        @Override
        public String toString() {
            return "RBNode{" +
                    "color=" + color +
                    ", key=" + key +
                    ", value=" + value +
                    '}';
        }
    }


    //**********************************************************

    @Override
    public Iterator<RBNode<K, V>> iterator() {
        return new RBTIterator<>(root);
    }

    /**
     * 中序遍历实现迭代器
     * 这里实现引入Stack,而非递归实现
     * @param <K>
     * @param <V>
     */
    public static class RBTIterator<K,V> implements Iterator<RBNode<K,V>> {
        private Stack<RBNode<K,V>> stack;
        public RBTIterator(RBNode<K, V> root) {
            stack = new Stack<>();
            addLeftPath(root);
        }

        private void addLeftPath(RBNode<K, V> p) {
            while (p != null) {
                stack.push(p);
                p = p.left;
            }
        }

        @Override
        public boolean hasNext() {
            return !stack.empty();
        }

        @Override
        public RBNode<K, V> next() {
            RBNode<K, V> entry = stack.pop();
            addLeftPath(entry.right);
            return entry;
        }
    }


    //**********************************************************


    /**
     * 层序遍历
     */
    public void levelOrder() {
        if(root == null) return;
        Queue<RBNode<K,V>> queue = new LinkedList<>();
        queue.offer(root);
        int preCount = 1;
        int pCount = 0;
        while (!queue.isEmpty()) {
            preCount --;
            RBNode<K,V> p = queue.poll();
            System.out.print(p + " ");
            if (p.left != null) {
                queue.offer(p.left);
                pCount ++;
            }
            if (p.right != null) {
                queue.offer(p.right);
                pCount ++;
            }
            if (preCount == 0) {
                preCount = pCount;
                pCount = 0;
                System.out.println();
            }
        }
    }

}

源码:
https://github.com/xiaojinwei/java-learning/blob/master/src/com/cj/learn/tree/rbt/RBTMap.java

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值