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

一、规则

1.每个节点要么是红色、要么是黑色
2.根节点一定是黑色
3.红色节点不可以连续出现(父节点、子节点不可同时为红)
4.从任意节点出发,到树底的所有路线,途径的黑节点数量必须相同
在修改红黑树的时候,切记要维护这个规则。一般默认插入红色节点(除非是root节点),插入后再进行旋转和颜色变换

二、旋转、变换技巧

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

1.插入root

不会违反规则

2.父黑

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

3.父红

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

4.父红,叔红

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

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

可是此时,如果曾节点是根节点的话,那么必须将曾节点转为黑色。
所以一般在修补红黑树的方法的最后,会强制将根节点转为黑色
这种情况可能又会导致5.1的情况

5.1父红,叔黑,外侧子孙

父节点是红色,叔节点是黑色或者null,且新增节点是曾节点的外侧子孙(违反规则3)
外侧子孙:A点是B点的左-左-....-左或者右-右-..-右,那么A是B的外侧子孙,否则A是B的内侧子孙
符号解释:
N:新插入的节点,P:父节点,G:曾节点,U:叔节点

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

不过此时颜色还有冲突,还要调整一下颜色
将P与G的颜色对调,既P变为黑色,G变为红色

总结一下,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只有一个内侧外侧的区别。
内侧子孙比外侧要多做一次旋转。目的是把内侧子孙旋转成外侧子孙来做

这里的N就是G的内侧子孙,现在要把N变成G的外侧子孙。
将P旋转(这里是左旋),让N旋转到P的位置

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

5.2.1内侧转外侧的旋转方向

根据P点和N的相对位置,当N是P的右节点,则将P左旋,否则将P右旋
目的是让N旋转到P的位置
总结情况:
1.插入的是根节点,不违反规则
2.插入点的父节点是黑色,不违反规则
3.插入点的父、叔节点是红色,将父、叔转成黑色,然后将曾节点转成红色
4.插入点的父是红色、叔节点是黑色或者null,通过旋转和转变颜色,注意内侧外侧子孙!

三、分析TreeMap源码

jdk中的红黑树实现是TreeMap。

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

    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;//强制将根节点转成黑色
}

查看原文:http://blog.zswlib.com/2016/11/01/jdk%e6%ba%90%e7%a0%81%e5%88%86%e6%9e%90%e7%ba%a2%e9%bb%91%e6%a0%91-%e6%8f%92%e5%85%a5%e7%af%87/

jdk源码分析红黑树——插入篇的更多相关文章

  1. HashMap1.8源码分析(红黑树)

    转载:https://segmentfault.com/a/1190000012926722?utm_source=tag-newest https://blog.csdn.net/weixin_40 ...

  2. 【JDK】JDK源码分析-TreeMap(2)

    前文「JDK源码分析-TreeMap(1)」分析了 TreeMap 的一些方法,本文分析其中的增删方法.这也是红黑树插入和删除节点的操作,由于相对复杂,因此单独进行分析. 插入操作 该操作其实就是红黑 ...

  3. 【JDK】JDK源码分析-LinkedHashMap

    概述 前文「JDK源码分析-HashMap(1)」分析了 HashMap 主要方法的实现原理(其他问题以后分析),本文分析下 LinkedHashMap. 先看一下 LinkedHashMap 的类继 ...

  4. 【JDK】JDK源码分析-HashMap(1)

    概述 HashMap 是 Java 开发中最常用的容器类之一,也是面试的常客.它其实就是前文「数据结构与算法笔记(二)」中「散列表」的实现,处理散列冲突用的是“链表法”,并且在 JDK 1.8 做了优 ...

  5. 【JDK】JDK源码分析-HashMap(2)

    前文「JDK源码分析-HashMap(1)」分析了 HashMap 的内部结构和主要方法的实现原理.但是,面试中通常还会问到很多其他的问题,本文简要分析下常见的一些问题. 这里再贴一下 HashMap ...

  6. JDK源码分析—— ArrayBlockingQueue 和 LinkedBlockingQueue

    JDK源码分析—— ArrayBlockingQueue 和 LinkedBlockingQueue 目的:本文通过分析JDK源码来对比ArrayBlockingQueue 和LinkedBlocki ...

  7. JDK 源码分析(4)—— HashMap/LinkedHashMap/Hashtable

    JDK 源码分析(4)-- HashMap/LinkedHashMap/Hashtable HashMap HashMap采用的是哈希算法+链表冲突解决,table的大小永远为2次幂,因为在初始化的时 ...

  8. 【JDK】JDK源码分析-List, Iterator, ListIterator

    List 是最常用的容器之一.之前提到过,分析源码时,优先分析接口的源码,因此这里先从 List 接口分析.List 方法列表如下: 由于上文「JDK源码分析-Collection」已对 Collec ...

  9. 【JDK】JDK源码分析-AbstractQueuedSynchronizer(1)

    概述 前文「JDK源码分析-Lock&Condition」简要分析了 Lock 接口,它在 JDK 中的实现类主要是 ReentrantLock (可译为“重入锁”).ReentrantLoc ...

随机推荐

  1. Vim使用

    模式 ESC\Ctrl+c:退出编辑模式 ZZ\wq:命令模式下保存退出 编辑 i:进入编辑模式 I:转到行首非空字符开始编辑 s:删除当前字符进入编辑模式 a:从当前字符后开始编辑 A:从当前行末非 ...

  2. x01.os.23: 制作 linux LiveCD

    1.首先运行如下命令  sudo apt-get install wget bc build-essential gawk genisoimage 2.下载如下资源,make all 即可 http: ...

  3. [异常解决] windows用SSH和linux同步文件&linux开启SSH&ssh client 报 algorithm negotiation failed的解决方法之一

    1.安装.配置与启动 SSH分客户端openssh-client和openssh-server 如果你只是想登陆别的机器的SSH只需要安装openssh-client(ubuntu有默认安装,如果没有 ...

  4. Integer.parseInt 引发的血案

    Integer.parseInt 处理一个空字符串, 结果出错了, 程序没有注意到,搞了很久, 引发了血案啊!! 最后,终于 观察到了, 最后的部分: Caused by: java.lang.NoC ...

  5. postman使用

    1.postman的下载:google首页左上角应用,点击后,如果没有下载postman,就在google商店搜索,点击右边按钮 2.下载后重新打开google首页,点击应用,可以看到已经下载过了,点 ...

  6. 使用Newtonsoft.Json.dll(JSON.NET)动态解析JSON、.net 的json的序列化与反序列化(一)

    在开发中,我非常喜欢动态语言和匿名对象带来的方便,JSON.NET具有动态序列化和反序列化任意JSON内容的能力,不必将它映射到具体的强类型对象,它可以处理不确定的类型(集合.字典.动态对象和匿名对象 ...

  7. C#提供APP接口之JSON差异

    C#在给APP提供接口,现在返回的数据大部分分为三类:JSON.XML.BTYE. 今天简单说下C#给APP提供接口返回JSON的一些异同: 1.通过Newtonsoft.Json.JsonConve ...

  8. python处理命令行参数

    直接从命令行执行py文件的时候如果带有参数,如何获取这些参数,如何解析? http://blog.chinaunix.net/uid-20786165-id-3182268.html sys.argv ...

  9. SQL Server中TOP子句可能导致的问题以及解决办法

    简介      在SQL Server中,针对复杂查询使用TOP子句可能会出现对性能的影响,这种影响可能是好的影响,也可能是坏的影响,针对不同的情况有不同的可能性.      关系数据库中SQL语句只 ...

  10. 计算机程序的思维逻辑 (50) - 剖析EnumMap

    上节我们提到,如果需要一个Map的实现类,并且键的类型为枚举类型,可以使用HashMap,但应该使用一个专门的实现类EnumMap. 为什么要有一个专门的类呢?我们之前介绍过枚举的本质,主要是因为枚举 ...