CHM-红黑树

  • putTreeVal
/**
 * Finds or adds a node.
 * @return null if added
 */
final TreeNode<K,V> putTreeVal(int h, K k, V v) {
    Class<?> kc = null;
    boolean searched = false;
    for (TreeNode<K,V> p = root;;) {                                //以根节点为当前节点
        int dir, ph; K pk;
        if (p == null) {                                            
            first = root = new TreeNode<K,V>(h, k, v, null, null);  //根节点为空,新建一个节点作为根节点,返回
            break;
        }
        else if ((ph = p.hash) > h)                                 //当前节点hash ph>h,则新节点应该在左侧,dir=-1
            dir = -1;
        else if (ph < h)
            dir = 1;                                                //当前节点 ph<h,则新节点应该在右侧,dir=1
        else if ((pk = p.key) == k || (pk != null && k.equals(pk))) //当前节点与新节点的key==或equals,则无需新建节点,直接返回当前节点
            return p;
        else if ((kc == null &&                                     //其他情况:hash相等,但key不一样
                  (kc = comparableClassFor(k)) == null) ||
                 (dir = compareComparables(kc, k, pk)) == 0) {      //key 未实现comparable接口,或key compare==0(dir重新赋值)
            if (!searched) {
                TreeNode<K,V> q, ch;
                searched = true;
                if (((ch = p.left) != null &&
                     (q = ch.findTreeNode(h, k, kc)) != null) ||
                    ((ch = p.right) != null &&
                     (q = ch.findTreeNode(h, k, kc)) != null))
                    return q;                                       //往左或往右查找,若找到与k相同key的节点,则返回
            }
            dir = tieBreakOrder(k, pk);                             //未找到,比较k和pk的identityHashCode大小,给dir重新赋值
        }

        TreeNode<K,V> xp = p;
        if ((p = (dir <= 0) ? p.left : p.right) == null) {          //根据dir,往左或往右移动,并继续循环遍历,直至叶子节点,即 p==null
            TreeNode<K,V> x, f = first;
            first = x = new TreeNode<K,V>(h, k, v, f, xp);          //新建节点x,并将first节点指向新建节点x
            if (f != null)                                          //将原来的first.prev指向新节点x
                f.prev = x;
            if (dir <= 0)
                xp.left = x;                                        //x为左节点
            else
                xp.right = x;                                       //x为右节点
            if (!xp.red)
                x.red = true;                                       //若父节点为黑色,当前新建节点涂为红色,直接插入即可
            else {
                lockRoot();                                         //否则需要平衡调整,此过程需要对根节点加锁
                try {
                    root = balanceInsertion(root, x);
                } finally {
                    unlockRoot();
                }
            }
            break;
        }
    }
    assert checkInvariants(root);
    return null;
}
  • balanceInsertion
static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root,
                                            TreeNode<K,V> x) {
    x.red = true;
    for (TreeNode<K,V> xp, xpp, xppl, xppr;;) {
        if ((xp = x.parent) == null) {                      //1、父节点为空,则将当前节点涂黑,并设为根节点,返回
            x.red = false;
            return x;
        }
        else if (!xp.red || (xpp = xp.parent) == null)      //2、父节点为黑色或父节点为根节点(祖父节点为空),无需调整,返回。
            return root;
        if (xp == (xppl = xpp.left)) {  //父节点是左节点
            if ((xppr = xpp.right) != null && xppr.red) {   //3、父节点是红色,叔节点也是红色
                xppr.red = false;                           //3-1、涂色:父节点、叔节点涂黑,祖父节点涂红
                xp.red = false;
                xpp.red = true;
                x = xpp;                                    //3-2、将祖父节点选为当前节点x=xpp,继续递归上溯调整
            }
            else {                                          //4.1、父节点是红色,叔节点是黑色,父节点是左节点
                if (x == xp.right) {                        //4.1.1 调整:当前节点是左节点的才需要,调整到同一侧(都为左节点)
                    root = rotateLeft(root, x = xp);        //      左旋:以父节点为支点左旋,目的是将x、xp、xpp全部都调整到左侧节点,此时链路为:xp <- x <- xpp
                                                            //      重选:x=xp,以最左侧节点为当前节点(即以父节点为当前节点)
                    xpp = (xp = x.parent) == null ? null : xp.parent;   //重新给xp, xpp赋值
                }
                if (xp != null) {                           
                    xp.red = false;                         //4.1.2 涂色和平衡
                    if (xpp != null) {
                        xpp.red = true;                     //      涂色:父节点涂黑,祖父节点涂红:x:red, xp:black, xpp:red,此时链路为:x <- xp <- xpp
                        root = rotateRight(root, xpp);      //      平衡:以祖父节点为支点右旋,目的是将x、xp、xpp三个节点重新平衡,此时链路为:x <- xp -> xpp
                                                            //注意:这一步右旋后不改变当前节点,进行下一次循环时父节点已经涂黑了xp:black,将跳出循环
                    }
                }
            }
        }
        else {                      //父节点是右节点,对称的操作
            if (xppl != null && xppl.red) {
                xppl.red = false;
                xp.red = false;
                xpp.red = true;
                x = xpp;
            }
            else {
                if (x == xp.left) {
                    root = rotateRight(root, x = xp);
                    xpp = (xp = x.parent) == null ? null : xp.parent;
                }
                if (xp != null) {
                    xp.red = false;
                    if (xpp != null) {
                        xpp.red = true;
                        root = rotateLeft(root, xpp);
                    }
                }
            }
        }
    }
}

balanceInsertion
当前插入节点默认涂红
1、父节点为空,则将当前节点涂黑,并设为根节点,返回。
2、父节点为黑色或父节点为根节点(祖父节点为空),无需调整,返回。
3、父节点是红色,叔节点也是红色
涂色:父节点、叔节点涂黑,祖父节点涂红
重选:将祖父节点选为当前节点x=xpp
原理:父节点和叔节点都是红色,说明以xpp为根的这棵子树下面的子孙是平衡的。所以,就先涂颜色:当前节点是红色,把父节点涂黑,为了保持平衡叔节点需要一起涂黑;
然后,将祖父节点选为当前节点并涂红(因为之前的当前节点即新加的节点默认是红色的),进行递归上溯调整
4、父节点是红色,叔节点是黑色
4.1、父节点是左节点
调整:当前节点是右节点的才需要
左旋:以父节点为支点左旋,目的是将x、xp、xpp全部都调整到左侧,此时链路为:xp <- x <- xpp
重选:以最左侧节点为当前节点(即以父节点为当前节点x=xp),以新的x重新给xp、xpp赋值,此时链路为:x <- xp <- xpp
涂色和平衡
涂色:父节点涂黑,祖父节点涂红:x:red, xp:black, xpp:red,此时链路为:x <- xp <- xpp
右旋:以祖父节点为支点右旋,目的是将x、xp、xpp三个节点重新平衡,此时链路为:x <- xp -> xpp
注意:这一步右旋后不改变当前节点,进行下一次循环时父节点已经涂黑了xp:black,将跳出循环
4.2、父节点是右节点
调整:当前节点是左节点的才需要
右旋:以父节点为支点右旋,目的是将x、xp、xpp全部都调整到右侧,此时链路为:xpp -> x-> xp
重选:以最右侧节点为当前节点(即以父节点为当前节点x=xp),以新的x重新给xp、xpp赋值,此时链路为:xpp -> xp -> x
涂色和平衡
涂色:父节点涂黑,祖父节点涂红:x:red, xp:black, xpp:red,此时链路为:xpp -> xp -> x
左旋:以祖父节点为支点左旋,目的是将x、xp、xpp三个节点重新平衡,此时链路为:xpp <- xp -> x
注意:这一步左旋后不改变当前节点,进行下一次循环时父节点已经涂黑了xp:black,将跳出循环
原理:父节点和叔节点颜色不一致,x节点插入后就有可能造成以xpp为根的子树不平衡,需要调整。
调整策略就是:先将x、xp、xpp拉到同一侧(都为左节点或都为右节点),以最下方节点(左侧为最左,右测为最右)为当前节点,将当前的父节点涂黑,祖父涂红并以祖父为支点旋转平衡。
调整后:链路为:x <- xp -> xpp 或 xpp <- xp -> x,颜色都是 红 - 黑 - 红,子树根节点是xp,调整后子树是平衡的且子树的根节点是黑色的。然后再以x节点继续循环,将走到第2步,结束调整。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容