JAVA中的数据结构 - 1,红黑树
背景:
在JDK源码中, 有treeMap和JDK8的HashMap都用到了红黑树去存储
红黑树可以看成B树的一种:
二叉树-->搜索二叉树-->平衡搜索二叉树-->B树--> 红黑树
故我提取出了红黑树部分的源码,去说明红黑树的理解
看之前,理解红黑树的几个特性,后面的操作都是为了让搜索二X树符合红黑树的这几个特性,从而满足对查找效率的O(logn)
红黑树特效
1,根和叶子节点都是黑色的
2,不能有有连续两个红色的节点
3, 从任一节点到它所能到达得叶子节点的所有简单路径都包含相同数目的黑色节点
这几个特效,个人理解就是规定了红黑树是一颗2-3-4的B树了,从而满足了O(logn)查找效率
改变方法,这些改变也都是在O(logn)内完成的
1, 改变颜色
2, 左旋
3, 右旋
从JDK源码来理解
先看TreeMap
//对treeMap的红黑树理解注解. 2017.02.16 by 何锦彬 JDK,1.7.51<br> <br>/** From CLR */
private void fixAfterInsertion(Entry<K, V> x) {
//新加入红黑树的默认节点就是红色
x.color = RED;
/**
* 1. 如为根节点直接跳出
*/
while (x != null && x != root && x.parent.color == RED) {
if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
//如果X的父节点(P)是其父节点的父节点(G)的左节点
//即 下面这种情况
/**
* G
* P(RED) U
*/
//获取其叔(U)节点
Entry<K, V> y = rightOf(parentOf(parentOf(x)));
if (colorOf(y) == RED) {
// 这种情况
/**
* G
* P(RED) U(RED)
* X
*/
//如果叔节点是红色的(父节点有判断是红色). 即是双红色,比较好办,通过改变颜色就行. 把P和U都设置成黑色然后,X加到P节点。 G节点当作新加入节点继续迭代
setColor(parentOf(x), BLACK);
setColor(y, BLACK);
setColor(parentOf(parentOf(x)), RED);
x = parentOf(parentOf(x));
} else {
//处理红父,黑叔的情况
if (x == rightOf(parentOf(x))) {
// 这种情况
/**
* G
* P(RED) U(BLACK)
* X
*/
//如果X是右边节点
x = parentOf(x);
// 进行左旋
rotateLeft(x);
}
//左旋后,是这种情况了
/**
* G
* P(RED) U(BLACK)
* X
*/
// 到这,X只能是左节点了,而且P是红色,U是黑色的情况
//把P和G改成黑色,以G为节点进行右旋
setColor(parentOf(x), BLACK);
setColor(parentOf(parentOf(x)), RED);
rotateRight(parentOf(parentOf(x)));
}
} else {
//父节点在右边的
/**
* G
* U P(RED)
*/
//获取U
Entry<K, V> y = leftOf(parentOf(parentOf(x)));
if (colorOf(y) == RED) {
//红父红叔的情况
/**
* G
* U(RED) P(RED)
*/
setColor(parentOf(x), BLACK);
setColor(y, BLACK);
setColor(parentOf(parentOf(x)), RED);
//把G当作新插入的节点继续进行迭代
x = parentOf(parentOf(x));
} else {
//红父黑叔,并且是右父的情况
/**
* G
* U(RED) P(RED)
*/
if (x == leftOf(parentOf(x))) {
//如果插入的X是左节点
/**
* G
* U(BLACK) P(RED)
* X
*/
x = parentOf(x);
//以P为节点进行右旋
rotateRight(x);
}
//右旋后
/**
* G
* U(BLACK) P(RED)
* X
*/
setColor(parentOf(x), BLACK);
setColor(parentOf(parentOf(x)), RED);
//以G为节点进行左旋
rotateLeft(parentOf(parentOf(x)));
}
}
}
//红黑树的根节点始终是黑色
root.color = BLACK;
}
再看看HashMap的实现,
在HashMap中,在JDK8后开始用红黑树代替链表,查找由O(n) 变成了 O(Logn)
源码分析如下:
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
//JDK8 的hashmap,链表到了8就需要变成颗红黑树了
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
红黑树的维护代码部分如下:
//hashmap的红黑树平衡
static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root,
TreeNode<K,V> x) {
x.red = true;
//死循环加变量定义,总感觉JAVA很少这样写代码 哈
for (TreeNode<K,V> xp, xpp, xppl, xppr;;) {
//xp X父节点, XPP X的祖父节点, XPPL 祖父左节点 XXPR 祖父右节点
if ((xp = x.parent) == null) {
x.red = false;
return x;
}
// 如果父节点是黑色, 或者XP父节点是空,直接返回
else if (!xp.red || (xpp = xp.parent) == null)
return root;
// 下面的代码就和上面的很treeMap像了,
if (xp == (xppl = xpp.left)) {
// 第一种情况, 赋值xppl
//父节点是左节点的情况,下面这种
/**
* G
* P(RED) U
*/
if ((xppr = xpp.right) != null && xppr.red) {
//如果红叔的情况
// 这种情况
/**
* G
* P(RED) U(RED)
* X
*/
//改变其颜色,
xppr.red = false;
xp.red = false;
xpp.red = true;
x = xpp;
}
else {
// 黑叔的情况
// 这种情况
/**
* G
* P(RED) U(BLACK)
*/
if (x == xp.right) {
//如果插入节点在右边 这种
// 这种情况
/**
* G
* P(RED) U(BLACK)
* X
*/
//需要进行左旋
root = rotateLeft(root, x = xp);
xpp = (xp = x.parent) == null ? null : xp.parent;
}
//左旋后情况都是这种了
/**
* G
* P(RED) U(BLACK)
* X
*/
// 到这,X只能是左节点了,而且P是红色,U是黑色的情况
if (xp != null) {
//把P和G改成黑色,以G为节点进行右旋
xp.red = false;
if (xpp != null) {
xpp.red = true;
root = rotateRight(root, xpp);
}
}
}
}
else {
//父节点在右边的
/**
* G
* U P(RED)
*/
//获取U
if (xppl != null && xppl.red) {
//红父红叔的情况
/**
* G
* U(RED) P(RED)
*/
xppl.red = false;
xp.red = false;
xpp.red = true;
x = xpp;
}
else {
if (x == xp.left) {
//如果插入的X是右节点
/**
* G
* U(BLACK) P(RED)
* X
*/
root = rotateRight(root, x = xp);
xpp = (xp = x.parent) == null ? null : xp.parent;
}
//右旋后
/**
* G
* U(BLACK) P(RED)
* X
*/
if (xp != null) {
xp.red = false;
if (xpp != null) {
xpp.red = true;
root = rotateLeft(root, xpp);
}
}
}
}
}
可见,其实处理逻辑都一样:
流程图如下:

先谈谈我对红黑树的理解
一, 从2-3-4树去理解
红黑树,其实是一颗 2-3-4的B树,如果这样理解就能知道为什么能O(logn)的查找了
可以把红色节点看成是连接父节点的组成的一个大节点,如下:
红色的就是和父节点组成了
1, B+树都是从底布开始往上生长,自动平衡,如 2-3-4树,当节点达到了3个时晋升到上个节点,所以不会产生单独生长一边的情况,形成平衡。
2-3-4树和红黑树的对应

(此图转自网上)
红色节点可以看成为和父节点组成的链接,比如上图的9 10 11,9和11都是红色,看成是两边的链接。
因为红黑树是一颗2-3-4树,故能保持相对的平衡,从而保证了操作的O(LgN)
欢迎关注我的公众号,重现线上各种BUG, 一起来构建我们的知识体系

JAVA中的数据结构 - 1,红黑树的更多相关文章
- Java中的TreeMap及红黑树
TreeMap: http://blog.csdn.net/tobeandnottobe/article/details/7232664 红黑树: http://blog.chinaunix.net/ ...
- 动图+源码,演示Java中常用数据结构执行过程及原理
最近在整理数据结构方面的知识, 系统化看了下Java中常用数据结构, 突发奇想用动画来绘制数据流转过程. 主要基于jdk8, 可能会有些特性与jdk7之前不相同, 例如LinkedList Linke ...
- 动图+源码,演示 Java 中常用数据结构执行过程及原理
阅读本文大概需要 3.7 分钟. 作者:大道方圆 cnblogs.com/xdecode/p/9321848.html 最近在整理数据结构方面的知识, 系统化看了下Java中常用数据结构, 突发奇想 ...
- Java中的数据结构-HashMap
Java数据结构-HashMap 目录 Java数据结构-HashMap 1. HashMap 1.1 HashMap介绍 1.1.1 HashMap介绍 1.1.2 HashMap继承图 1.2 H ...
- Java中常见数据结构:list与map -底层如何实现
1:集合 2 Collection(单列集合) 3 List(有序,可重复) 4 ArrayList 5 底层数据结构是数组,查询快,增删慢 6 线程不安全,效率高 7 Vector 8 底层数据结构 ...
- Linux 内核里的数据结构:红黑树(rb-tree)
转自:https://www.cnblogs.com/slgkaifa/p/6780299.html 作为一种数据结构.红黑树可谓不算朴素.由于各种宣传让它过于神奇,网上搜罗了一大堆的关于红黑树的文章 ...
- 物联网安全himqtt防火墙数据结构之红黑树源码分析
物联网安全himqtt防火墙数据结构之红黑树源码分析 随着5G的发展,物联网安全显得特别重要,himqtt是首款完整源码的高性能MQTT物联网防火墙 - MQTT Application FireWa ...
- java中的数据结构(集合|容器)
对java中的数据结构做一个小小的个人总结,虽然还没有到研究透彻jdk源码的地步.首先.java中为何需要集合的出现?什么需求导致.我想对于面向对象来说,对象适用于描述任何事物,所以为了方便对于对象的 ...
- [转]详细介绍java中的数据结构
详细介绍java中的数据结构 本文介绍的是java中的数据结构,本文试图通过简单的描述,向读者阐述各个类的作用以及如何正确使用这些类.一起来看本文吧! 也许你已经熟练使用了java.util包里面的各 ...
- Java中常见数据结构:list与map
1:集合 Collection(单列集合) List(有序,可重复) ArrayList 底层数据结构是数组,查询快,增删慢 线程不安全,效率高 Vector 底层数据结构是数组,查询快,增删慢 线程 ...
随机推荐
- iOS Run loop使用实例
http://blog.csdn.net/libaineu2004/article/details/45364737 一.Runloop简介: Run loops 是线程相关的的基础框架的一部分.一个 ...
- jQuery源码学习(2):选择器初窥
选择器初窥 代码架构: jQuery选择器可以依照传入数据的类型分为五大类: 传入字符串:$("div"), $("#id"), $(".div1&q ...
- Zookeeper的基本概念
Reference: http://mp.weixin.qq.com/s?src=3×tamp=1477979201&ver=1&signature=bBZqNrN ...
- Android布局及属性归总(查询用)
常见布局 LinearLayout 线性布局 子元素任意,组织成一个单一的水平或垂直行,默认为水平方向TableLayout 表格布局 子元素为<Tabl ...
- Xcode7中 添加 .dylib
转一个 Xcode 7 缺少 *.dylib库的解决方法 Xcode7中 Link Binary With Libraries 没有 .dylib库,只能找到对应的 .tbd,但不能代替使用,通过查找 ...
- CSS BUG 总结
1.IE7 容器使用了滚动条 其子元素中使用 position:relative ,position变成了fixed,从而不随容器的滚动条滚动; 解决: 在其容器元素的属性中也加入 position ...
- Tamper Data 安装与使用
Tamper Data概览 注意:我将会讲述一些有关Tamper Data的基本常识,包括它的基本功能,如何安装等. Tamper Data是什么? Tamper Data 的真实含义,即&q ...
- 支付宝开发中return_url和notify_url的区别分析
在处理支付宝业务中出现过这样的问题,付费完成后,在支付宝跳转到商家指定页面时,订单状态已经更新,通过调试发现是支付宝先通知notify_url,完成了订单状态. 支付宝return_url和notif ...
- jackson - 生成jason工具-简单示例
主页: http://jackson.codehaus.org/ https://github.com/FasterXML/jackson 当前jackson分为三部分,需要分别下载: jackson ...
- 小试 Ninja
Ninja 是最近冒出来的一个 build system,它很像 make,然而效率更高,对大项目支持更好.当然我用 Ninja 和效率无关(我又没有那种有几百个中间目标的 C++ 项目要 build ...