博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
jdk源码分析红黑树——插入篇
阅读量:5972 次
发布时间:2019-06-19

本文共 3145 字,大约阅读时间需要 10 分钟。

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

一、规则

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

二、旋转、变换技巧

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

1.插入root

不会违反规则

2.父黑

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

3.父红

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

4.父红,叔红

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

1

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

2

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

5.1父红,叔黑,外侧子孙

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

3

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

4

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

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

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

7

之后的步骤就是和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
x) { x.color = RED; while (x != null && x != root && x.parent.color == RED) {//父节点是红色 if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {//父节点是曾节点的左节点 Entry
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
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;//强制将根节点转成黑色 }

查看原文:

转载于:https://www.cnblogs.com/wewill/p/6020978.html

你可能感兴趣的文章
扩展Spring Cloud Feign 实现自动降级
查看>>
代码片段
查看>>
jsonp跨域资源引起CORB
查看>>
鼠标右键兼容MAC版火狐浏览器
查看>>
web安全类
查看>>
Vue项目中使用基于pdf.js的vue-pdf插件在pc浏览器下阅览PDF文件
查看>>
编译VIM
查看>>
玩转Elasticsearch源码-一图看懂ES启动流程
查看>>
自动注册appleid
查看>>
OpenTracing Registry登记了129个仪器插件和追踪器
查看>>
使用 Gatsby.js 搭建静态博客 7 文章目录
查看>>
logback.xml日志写入数据库改造,重写源码手工读取yml参数作为数据源参数的方法...
查看>>
一个完整的增删改查模块(以我们的项目‘危化品库管理’模块为例)
查看>>
github GPG 配置
查看>>
javascript 多物体运动
查看>>
JavaScript 之 DOM [ Document对象 ]
查看>>
koa源码阅读[3]-koa-send与它的衍生(static)
查看>>
【spring 注解】第2篇:@ComponentScan
查看>>
从零开始实现一个IDL+RPC框架
查看>>
Docker 容器监控系统初探
查看>>