前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >jdk源码分析红黑树——插入篇1.插入root2.父黑3.父红4.父红,叔红5.1父红,叔黑,外侧子孙5.2父红,叔黑,内侧子孙

jdk源码分析红黑树——插入篇1.插入root2.父黑3.父红4.父红,叔红5.1父红,叔黑,外侧子孙5.2父红,叔黑,内侧子孙

作者头像
用户1174983
发布2018-02-05 15:25:57
6550
发布2018-02-05 15:25:57
举报
文章被收录于专栏:钟绍威的专栏钟绍威的专栏

红黑树是自平衡的排序树,自平衡的优点是减少遍历的节点,所以效率会高。如果是非平衡的二叉树,当顺序或逆序插入的时候,查找动作很可能会遍历n个节点

红黑树的规则很容易理解,但是维护这个规则难。

一、规则

1.每个节点要么是红色、要么是黑色

2.根节点一定是黑色

3.红色节点不可以连续出现(父节点、子节点不可同时为红)

4.从任意节点出发,到树底的所有路线,途径的黑节点数量必须相同

在修改红黑树的时候,切记要维护这个规则。一般默认插入红色节点(除非是root节点),插入后再进行旋转和颜色变换

二、旋转、变换技巧

关于旋转和颜色变换有几种情况:

1.插入root

不会违反规则

2.父黑

插入节点的父节点是黑色,不会违反规则

3.父红

插入节点的父节点是红色,一定违反规则(违反规则3)(因为默认插入红色节点),此时就需要修复红黑树了。这种情况下又细分为几种情况

4.父红,叔红

父节点和叔节点均为红色(违反规则3)

1
1

这种情况下就把父节点和叔节点都改成黑色,然后曾节点改为红色

2
2

可是此时,如果曾节点是根节点的话,那么必须将曾节点转为黑色。

所以一般在修补红黑树的方法的最后,会强制将根节点转为黑色

这种情况可能又会导致5.1的情况

5.1父红,叔黑,外侧子孙

父节点是红色,叔节点是黑色或者null,且新增节点是曾节点的外侧子孙(违反规则3)

外侧子孙:A点是B点的左-左-....-左或者右-右-..-右,那么A是B的外侧子孙,否则A是B的内侧子孙

符号解释:

N:新插入的节点,P:父节点,G:曾节点,U:叔节点

3
3

此时就需要将G点旋转,这里是右旋

4
4

不过此时颜色还有冲突,还要调整一下颜色

将P与G的颜色对调,既P变为黑色,G变为红色

5
5

总结一下,5.1的步骤:

step1:P->黑

step2:G->红

step3:将G旋转

5.1.1关于旋转的方向

旋转有左旋右旋,具体是看父节点是曾节点的左节点还是右节点

P是G的左节点,那么就将G右旋(因为要把P移动到G的位置)

P....G....右节点.....................G左旋

5.2父红,叔黑,内侧子孙

父节点是红色,叔节点是黑色或者null,且新增节点是曾节点的内侧子孙(违反规则3)

外侧子孙:A点是B点的左-左-....-左或者右-右-..-右,那么A是B的外侧子孙,否则A是B的内侧子孙

5.2跟5.1只有一个内侧外侧的区别。

内侧子孙比外侧要多做一次旋转。目的是把内侧子孙旋转成外侧子孙来做

6
6

这里的N就是G的内侧子孙,现在要把N变成G的外侧子孙。

将P旋转(这里是左旋),让N旋转到P的位置

7
7

之后的步骤就是和5.1处理外侧子孙的一样了。

5.2.1内侧转外侧的旋转方向

根据P点和N的相对位置,当N是P的右节点,则将P左旋,否则将P右旋

目的是让N旋转到P的位置

总结情况:

1.插入的是根节点,不违反规则

2.插入点的父节点是黑色,不违反规则

3.插入点的父、叔节点是红色,将父、叔转成黑色,然后将曾节点转成红色

4.插入点的父是红色、叔节点是黑色或者null,通过旋转和转变颜色,注意内侧外侧子孙!

三、分析TreeMap源码

jdk中的红黑树实现是TreeMap。

每次put之后,都会调用这个方法去维护红黑树的规则

代码语言:javascript
复制
    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) {//父、叔节点都是红色,情况4
	                //将父节点和叔节点变为黑色,曾节点变为红色
                    setColor(parentOf(x), BLACK);
                    setColor(y, BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    x = parentOf(parentOf(x));
                } else {//父节点是红色,叔节点是黑色或者null
                    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;//强制将根节点转成黑色
	}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2016-11-01 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、规则
  • 二、旋转、变换技巧
    • 1.插入root
      • 2.父黑
        • 3.父红
          • 4.父红,叔红
            • 5.1父红,叔黑,外侧子孙
              • 5.1.1关于旋转的方向
            • 5.2父红,叔黑,内侧子孙
              • 5.2.1内侧转外侧的旋转方向
          • 三、分析TreeMap源码
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档