Java数据结构:树之红黑树

1. 概述

在前面的篇章已经写了Java数据结构:树基础及二叉树,对于树已经有了一定的了解。而红黑树在树中是比较重要的一种,运用也比较多,比如HashMap的源码中就用到了红黑树。通过此篇对红黑树的了解,后续会写一篇关于HashMap源码的解析。

2. 红黑树的定义:

红黑树,R-B Tree,全称是Red-Black Tree,又称为“红黑树”。它一种特殊的二叉查找树,它满足二叉查找树的特征:任意一个节点所包含的键值,大于等于左孩子的键值,小于等于右孩子的键值。 红黑树的每个节点上都有存储位表示节点的颜色,可以是红(Red)或黑(Black)。

3. 红黑树的性质

a. 每个节点或者是黑色,或者是红色;

b. 根节点是黑色;

c. 每个叶子节点是黑色;这里叶子节点,是指为空(NULL)的叶子节点;

d. 如果一个节点是红色的,则它的子节点必须是黑色的,反之则不一定成立;

e.从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。

性质c 所指的叶子节点(NULL 节点),如上图所示,它不包含数据而只充当树在此结束的指示。这些节点在绘图中经常被省略,省略后的图如下所示:

4. 红黑树的旋转操作

红黑树的基本操作是添加、删除。在对红黑树进行添加或删除之后,都会用到旋转方法。这是因为添加或删除红黑树中的节点之后,红黑树就发生了变化,可能不满足红黑树的5条性质,也就不再是一颗红黑树了,而是一颗普通的树。而通过旋转、变色,可以使这颗树重新成为红黑树。旋转操作分为左旋和右旋。

4.1 左旋

以某个结点作为支点(旋转结点),其右子结点变为旋转结点的父节点,右子节点的左子节点变为旋转结点的右子节点,左子节点保持不变。

如图所示,G点经过了左旋,G的右子节点K变成了G的父节点,K原来的左子节点变成了G的右子节点,G的左子节点保持不变。

4.2 右旋

以某个结点作为支点(旋转结点),其左子节点变为旋转结点的父节点,左子结点的右子结点变为旋转节点的左子节点,右子节点保持不变。

如图所示,G点经过了右旋,G的左子节点E变成了G的父节点,E的原右子节点F变成了G的的左子节点,G的右子节点保持不变。

4.3 变色

结点的颜色由红变黑或由黑变红。当红黑树进行插入或者删除操作时候,为了继续满足红黑树的特性,经过旋转操作后,依然无法满足,就必须进行变色操作。变色的过程如下所示:

5. 红黑树的查找

红黑树的查找步骤如下:

a. 从根节点开始查找,把根节点设置为当前节点;

b. 若当前节点为空,返回 null;

c. 若当前节点不为空,用当前节点的 value 跟查找的 value 做比较;

d. 若当前节点的 value 等于查找的 value,则此节点就是查找目标,返回当前节点;

e. 若当前节点的 value 小于查找的 value,把当前节点的右子节点设置为当前节点,从步骤b 开始继续查找; 

f. 若当前节点的 value 大于查找的 value,把当前节点的左子节点设置为当前节点,从步骤b 开始继续查找;

查找如下所示:

6. 红黑树的插入

插入操作包括两部分:一查找插入的位置;二插入后自平衡。查找插入的父节点跟查找操作区别不大。

红黑树插入新节点,默认插入节点颜色都为红色,这是因为在插入新节点时候,如果插入的节点是黑色,那么这个节点所在的路径比其他路径多出了一个黑色节点,这个调整起来比较麻烦。如果插入的是红色节点,此时所有路径上的黑色节点数量不变,仅可能出现两个连续的红色节点的情况,这时候通过旋转和变色进行调整就可以。

在上图中,插入的数为145, 首先跟根节点100比较,145大于100,则与根节点的右节点140比较,145 仍然比140大,则与140的右节点200比较,140比200的值小,则与200的左节点168比较,145的值比168小,则继续与168的左节点比较,而168也已经没有子节点了,所以145变成168的左节点。当插入后,发现存在两个连续的红色节点,这时候为了继续满足红黑树的特性,对节点进行了旋转以及变色,从而继续满足红黑树。

7. 红黑树的删除

删除子节点:

删除父节点:

在红黑树的删除过程中,首先进行搜索查找,这里的查找与 5. 红黑树的查找 中的查找步骤一致,如果查找不到要删除的节点,则返回。如果有查找到要删除的节点,则分为以下几种情况:

1. 要删除的节点为子节点,这里的子节点指为非Null的节点,这种情况的话,直接删除该节点即可;

2. 要删除的节点为父节点,且只有一个子节点,则删除该节点,并用唯一的子节点替换该节点的位置,如果满足不了红黑树的特性,则经过变色旋转等操作,从而满足红黑树的特性;

3. 要删除的节点为父节点,且有两个子节点。则要先找到欲删除节点的后继节点。后继节点的寻找过程如下:从该点的右子节点开始,如果有左子节点则跳到左子节点,层层向下,直到某个子节点没有左子节点为止。实际上就是找到比要删除节点的关键字值大的集合中的最小值。将要删除节点与后继节点中的数据对换,经过数据交换之后,要删除的数据变成了一个叶子节点或者只有一个子节点的节点,这种情况就是上面的第1,2种情况。

8. 总结

此篇文章只是讲关于红黑树的相关理论,而具体的代码实现,在JDK的源码中TreeMap的原理就是红黑树的实现,后续的将会整理一篇关于TreeMap的源码解析。

参考:

https://github.com/LRH1993/android_interview/blob/master/data-structure/tree/other-tree.md

https://blog.csdn.net/u012152619/article/details/46825969

https://blog.csdn.net/u012152619/article/details/47129179

https://www.jianshu.com/p/e136ec79235c

https://segmentfault.com/a/1190000014037447

https://www.cs.usfca.edu/~galles/visualization/RedBlack.html

https://blog.csdn.net/weixin_39651041/article/details/80037849

https://www.jianshu.com/p/d26d0de6a71f

https://blog.csdn.net/v_JULY_v/article/details/6105630

https://juejin.im/entry/58371f13a22b9d006882902d

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值