红黑树实现

一.红黑树规则

  1. 每个节点要么是红色,要么是黑色。
  2. 根节点必须是黑色
  3. 红色节点不能连续(也即是,红色节点的孩子和父亲都不能是红色)。
  4. 对于每个节点,从该点至null(树尾端)的任何路径,都含有相同个数的黑色节点。(插入新值时,其一定为红色节点。否则不符合4条件)

二.插入节点

1. 红黑树的平衡

红黑树的平横有三种方法
1.左旋
2.右旋
3.改变节点颜色

2. 左旋
左旋
3. 右旋
右旋
4.红黑树插入的三种情况

(1)case 1 新插入节点的父节点是红色,其父节点的兄弟节点也是红色时,通过变色操作调整平衡


1.gif

(2)case2 新插入节点的父节点为红色,父节点的兄弟节点为黑色时,且父节点、祖父节点和新节点3个节点在同一方向上(一条直线)


2.gif

(3)case3 新插入节点的父节点为红色,父节点的兄弟节点为黑色时,且父节点、祖父节点和新节点3个节点不在同一方向上(不是一条直线)该种情况需要旋转2次。
3.gif

总结:所以红黑树插入会有6种情况,插入时父节点在祖父节点的左边或者在祖父节点右边。并分别有case1-case3三种情况,加起来即六种情况

5 .put 方法实现(来自jdk1.8treemap源码)
    Node<K,T> root;
    static final boolean RED=true;
    static final boolean BLACK=false;

    public boolean put(K key,T object)
    {
        if(root == null)
        {
            root = new Node<>(key,object,null);
            root.color=BLACK;
            return true;
        }
        else
        {
            int cmp;
            Node<K,T> parent;
            Node<K,T> t = root;
            //比较key值的大小
            Comparable<? super K> tComparabler = (Comparable<? super K>) key;
            do
            {
                parent = t;
                cmp = tComparabler.compareTo(t.key);
                if(cmp < 0)
                {
                    t = t.left;
                }
                else if(cmp > 0)
                {
                    t = t.right;
                }
                else
                {
                    t.object = object;
                    return  true;
                }
            }
            while (t != null);
            Node<K,T> targetNode = new Node<>(key,object, parent);
            if(cmp > 0)
            {
                parent.right = targetNode;
            }
            else if(cmp < 0)
            {
                parent.left = targetNode;
            }
            //平衡红黑树
            fixAfterInsertion(targetNode);
        }

        return true;
    }
6.平衡的实现(来自jdk1.8treemap源码)
    private void fixAfterInsertion(Node<K,T> x)
    {
         x.color=RED;
         while (x!=null&&x!=root&&x.parent.color==RED)
         {
         if(parentof(x)==leftOf(parentof(parentof(x))))
         {
             Node<K,T> y = rightOf(parentof(parentof(x)));
             if(colorOf(y)==RED)      //case 1
             {
                 setColor(parentof(x),BLACK);
                 setColor(y, BLACK);
                 setColor(parentof(parentof(x)),RED);
                 x=parentof(parentof(x));
             }else {
                 if(x==rightOf(parentof(x))) //case 3;
                 {
                     x=parentof(x);
                     rotalLeft(x);
                 }
                 setColor(parentof(x),BLACK); //case 2;
                 setColor(parentof(parentof(x)),RED);
                 rotalRight(parentof(parentof(x)));
             }
         }
         else {
             Node<K,T> y = leftOf(parentof(parentof(x)));
             if(colorOf(y)== RED)
             {
                 setColor(parentof(x),BLACK);
                 setColor(parentof(parentof(x)),RED);
                 x=parentof(parentof(x));
             }
             else
             {
                 if(x==leftOf(parentof(x)))
                 {
                     x=parentof(x);
                     rotalRight(x);
                 }
                 setColor(parentof(x),BLACK);
                 setColor(parentof(parentof(x)),RED);
                 rotalLeft(parentof(parentof(x)));
             }
         }
         }
         root.color=BLACK;

    }
7.左旋,右旋实现(来自jdk1.8treemap源码)
  private void rotalRight(Node<K,T> x)
    {
        if(x.left==null)
        {
            throw new RuntimeException("不能右旋,左子节点为空");
        }
        Node<K,T> left = x.left;
        if(parentof(x)==null)
        {
            root=left;
        }else if(leftOf(parentof(x))==x)
        {
            parentof(x).left=left;
        }
        else if(rightOf(parentof(x))==x)
        {
            parentof(x).right=left;
        }
        left.parent=x.parent;
        x.parent=left;
        x.left=left.right;
        if(left.right!=null)
        {
            left.right.parent=x;
        }
        left.right=x;
    }

    private void rotalLeft(Node<K,T> x)
    {
        if(x.right==null)
        {
            throw new RuntimeException("不能左旋,右子节点为空");
        }
        Node<K,T> right = x.right;
        if(parentof(x)==null&&root==x)
        {
            root=right;
        }else if(x==rightOf(parentof(x)))
        {
            parentof(x).right=right;
        }else if(x==leftOf(parentof(x)))
        {
            parentof(x).left=right;
        }
        right.parent=x.parent;
        x.parent=right;
        x.right=right.left;
        if(right.left!=null){
            right.left.parent=x;
        }
        right.left=x;
    }

三.删除节点

  1. 删除节点的四种情况
    1. 待删除节点的兄弟节点是红色。
    2. 待删除节点的兄弟节点是黑色,且兄弟节点的子节点都是黑色。
    3. 待删除节点的兄弟节点是黑色,如果兄弟节点在右侧,且兄弟节点的左
      子节点是红色,右子节点是黑色。如果兄弟节点在左侧,就是兄弟节点的
      右子节点是红色,左节点是黑色。
    4. 待删除节点的兄弟节点是黑色,如果兄弟节点在右侧,且兄弟节点的右子节点是红色,左子节点是任意颜色。如果兄弟节点在左侧,则就是兄弟节点的左子节点是红色,右子节点是任意色。
  2. 动图演示
    case 1:对于 Case 1,首先改变父节点和兄弟节点颜色(兄弟节点变为黑色,父节点变为红色),再对父节点做一次旋转(红色的兄弟节点在右侧,左旋。红色的兄弟节点在左侧,右旋),操作后,红黑的规则没有被破坏,树的黑色高度没变化,原兄弟节点的一个子节点变成删除节点的新兄弟节点。
1.gif

case 2:对于Case 2,删除节点后其父节点的左子树比右子树黑高度小1,此时需要把兄弟节点改为红色,则左右子树黑高度就相同了。此时将删除节点的父节点变为新的节点,然后继续向上迭代调整,修正平衡


2.gif

case 3:对于Case 3,兄弟节点在右侧时,交换兄弟节点和其左子节点(红色)的颜色,并对兄弟节点进行右旋,于是被删除节点的新兄弟是一个有红色右子节点的黑色节点。相反,兄弟节点在左侧时,交换兄弟节点和其右子节点(红色)的颜色,并对兄弟节点进行左旋,于是被删除节点的新兄弟是一个有红色左子节点的黑色节点


3.gif

case 4:对于Case 4,兄弟节点在右侧时,兄弟节点和父节点交换颜色,把兄弟节点的右子节点设为黑色,并对删除节点的父节点做一次左旋转。
兄弟节点在左侧时,兄弟节点和父节点交换颜色,把兄弟节点的左子节点设为黑色,并对删除节点的父节点做一次右旋转。(经过case4后,红黑树完全平衡)

最后将删除节点直接指向根节点,循环结束。


4.gif
  1. 删除节点平衡红黑树的代码演示(来着jdk1.8threemap源码)
    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);  //情况1
                 rotateLeft(parentOf(x));
                 sib = rightOf(parentOf(x));
             }
    
             if (colorOf(leftOf(sib))  == BLACK &&
                 colorOf(rightOf(sib)) == BLACK) {
                 setColor(sib, RED);          //情况2
                 x = parentOf(x);
             } else {
                 if (colorOf(rightOf(sib)) == BLACK) {
                     setColor(leftOf(sib), BLACK);
                     setColor(sib, RED);    //情况3
                     rotateRight(sib);
                     sib = rightOf(parentOf(x));
                 }
                 setColor(sib, colorOf(parentOf(x)));
                 setColor(parentOf(x), BLACK);
                 setColor(rightOf(sib), BLACK); 情况4
                 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);
    

    }

ps:动图来源地址:https://www.cnblogs.com/newobjectcc/p/11295652.html

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