十年网站开发经验 + 多家企业客户 + 靠谱的建站团队
量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决
小编给大家分享一下Java数据结构之红黑树的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!
十多年的蕉城网站建设经验,针对设计、前端、开发、售后、文案、推广等六对一服务,响应快,48小时及时工作处理。成都营销网站建设的优势是能够根据用户设备显示端的尺寸不同,自动调整蕉城建站的显示方式,使网站能够适用不同显示终端,在浏览器中调整网站的宽度,无论在任何一种浏览器上浏览网站,都能展现优雅布局与设计,从而大程度地提升浏览体验。成都创新互联从事“蕉城网站设计”,“蕉城网站推广”以来,每个客户项目都认真落实执行。
一、红黑树所处数据结构的位置:
在JDK源码中, 有treeMap和JDK8的HashMap都用到了红黑树去存储
红黑树可以看成B树的一种:
从二叉树看,红黑树是一颗相对平衡的二叉树
二叉树-->搜索二叉树-->平衡搜索二叉树--> 红黑树
从N阶树看,红黑树就是一颗 2-3-4树
N阶树-->B(B-)树
故我提取出了红黑树部分的源码,去说明红黑树的理解
看之前,理解红黑树的几个特性,后面的操作都是为了让树符合红黑树的这几个特性,从而满足对查找效率的O(logn)
二、红黑树特性,以及保持的手段
1.根和叶子节点都是黑色的
2.不能有有连续两个红色的节点
3.从任一节点到它所能到达得叶子节点的所有简单路径都包含相同数目的黑色节点
这几个特效,个人理解就是规定了红黑树是一颗2-3-4的B树了,从而满足了O(logn)查找效率
保持特性的手段,通过下面这些手段,让红黑树满足红黑树的特性,如果要尝试理解,可以从2-3-4树的向上增长,后面有详细介绍
当然,这些改变也都是在O(logn)内完成的,主要改变方式有
1.改变颜色
2.左旋
3.右旋
三、从JDK源码来理解
主要看我的注释,逻辑的理解
先看TreeMap
//对treeMap的红黑树理解注解. 2017.02.16 by 何锦彬 JDK,1.7.51
/** From CLR */ private void fixAfterInsertion(Entryx) { //新加入红黑树的默认节点就是红色 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 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 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的红黑树平衡 staticTreeNode balanceInsertion(TreeNode root, TreeNode x) { x.red = true; //死循环加变量定义,总感觉JAVA很少这样写代码 哈 for (TreeNode 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) { //把P改成黑色,G改成红色, xp.red = false; if (xpp != null) { xpp.red = true; //以G节点左旋 root = rotateLeft(root, xpp); } } } } }
情况图如下
情况1
情况2
情况3
JDK源码处理红黑树的流程图
可见,其实处理逻辑实现都一样的
三、个人对红黑树理解的方法
1. 如何理解红黑树的O(lgN)的特性?
从2-3-4树去理解
红黑树,其实是一颗 2-3-4的B树,B树都是向上增长的,如果不理解向上增长可以先看看2-3树,这样理解就能知道为什么能O(logn)的查找了
2.如何理解红黑树的红黑节点意义?
可以把红色节点看成是连接父节点的组成的一个大节点(2个或3个或4个节点组成的一个key),如下:
(此图转自网上)
红色的就是和父节点组成了大节点,
比如
节点7和6,6是红色节点组成,故和它父节点7组成了一个大节点,即 2-3-4树的 6, 7节点
又如
节点 9和10和11,9和10为红色节点,故和10组成了一个2-3-4的3阶节点, 9,10,11(注意顺序有的关性)
3.B树是如何保持O(lgn)的复杂度的呢?
B+树都是从底布开始往上生长,自动平衡,如 2-3-4树,当节点达到了3个时晋升到上个节点,所以不会产生单独生长一边的情况,形成平衡。
以上是“Java数据结构之红黑树的示例分析”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注创新互联行业资讯频道!