6.4 红黑树

红黑树特征

  红黑树的出现,并不是无缘无故的。前面我讲了AVL树,但是在实际的应用中,AVL树并不多见,是因为什么呢?原因还是因为性能,AVL树查找性能肯定优秀,因为特别平衡,但是插入和删除操作因为频繁再平衡,所以性能稍微有点差。在这个背景下,1972年计算机科学家Bayer发明了红黑树。红黑树的思想来源于2-3-4树,有以下五个特征:
  1 节点是红色或者黑色;
  2 根总是黑色的;
  3 所有叶子都是黑色(叶子是NULL节点);
  4 如果节点是红色的,那么子节点必须是黑色;
  5 从根到叶子或者空叶子的每条路径,必须含有相同数目的黑节点。
  例如以下是经典的红黑树
在这里插入图片描述

  从上述五个特征看红黑树最差的左右子树不平衡情况,深度相差不会超过两倍。这种极端情况就是左子树一黑一红交替,右子树全黑。

1 插入算法

  插入时,直接插入一个新的红色节点。因为新的节点为红色只会违背特性4。也只有违背特性4的时候才需要对红黑树做出调整。而违背特性4只有一种情况,就是父节点的颜色为红色。

场景分类

伯父或叔叔红色伯父或叔叔黑色
LL型(/)着色着色,右旋
LR型(<)着色左旋,转换为LL(/)型
RR型(\)着色着色,左旋
RL型(>)着色右旋,转换为RR(\)型

  这就分为很多种子场景(以下以字母B代表黑色,字母R代表红色),可以分为五种场景:
  Case 1. 叔伯红色
  Case 2. 叔伯黑色,LL型
  Case 3. 叔伯黑色,LR型
  Case 4. 叔伯黑色,RR型
  Case 5. 叔伯黑色,RL型

场景一 叔伯红色

  选用测试数据{10, 8, 4, 5, 2, 1, 9, 6, 3, 7},会出现以下场景:
在这里插入图片描述
  调整为:
在这里插入图片描述
  这种场景,在AVL树里是平衡的,但是在红黑树中是不平衡,因为图中4和10都是红色。为了保证黑色高度不变,只需要祖父变成红色,然后将父亲和伯父变成黑色,这样黑色高度就不变,而且没有两个连续的红色。
  这个时候,祖父改成红色后,因为祖父的上级可能是红色,所以需要对祖父重新进行判断。
  如果祖父是root就无所谓了,因为把root改成红色后,再改回黑色就行了。
  Root由红改成黑,整体黑色高度增加,但是还是符合红黑树的规则。

场景二 LL型伯父黑色

  LL型,伯父是黑色或不存在,如下图:
在这里插入图片描述
  这个树是符合黑色高度规范的,因为整个树只有root一个黑节点,所以每个路径黑色高度都是一致的。
  但是连续两个红色,不符合红黑树不能连续两个红色的规定。
  这个时候呢,需要做一点复杂的操作。
  改颜色
在这里插入图片描述

  再右旋
在这里插入图片描述

  测试数据:6, 9, 5, 1, 8, 7, 4, 2, 10, 3

场景三 LR型伯父黑色

  LR型,或者<号型。如图(null节点视为黑色):
在这里插入图片描述
  这个时候只需要左旋转换为LL型,如图:
在这里插入图片描述

  测试数据:{3, 1, 5, 6, 9, 7, 8, 4, 2, 10}
  注意,这个转换后的不是第一种场景,是第三种场景。

场景四 RR型叔叔黑色

在这里插入图片描述
  着色后,4和5的颜色都已经改变:
在这里插入图片描述
  左旋后
在这里插入图片描述

  测试数据:{7, 10, 1, 4, 9, 3, 5, 8, 2, 6}

场景五 RL型叔叔黑色

  测试数据:{1, 3, 9, 4, 2, 10, 8, 7, 5, 6}。注意,图中是3-9-7三个元素形成一个RL型。
在这里插入图片描述
  旋转后,3-7-9这三个元素变成了RR型

2 删除算法

  删除算法,远比插入算法复杂。
  首先考虑删除红节点的情况,因为删除红节点,不会影响黑色高度。也会产生两级连续的红节点,所以直接用排序二叉树的删除算法就行了。
  而删除黑节点是最复杂的。因为降低了一个黑色高度,所以必须整体降低一个黑色高度。
  红黑树的删除与AVL树的删除算法有点不同。
  AVL树是先删除后调整。
  当然红黑树也可以先删除后调整,但是如果这样写代码会比较复杂。对于初学者,我建议是先调整再删除,虽然性能上会有点差,但是能更好地理解红黑树的删除算法。
  Java的TreeMap是先删除后调整的。
  红黑树的删除算法设计到两个节点:删除节点和替换节点。
  怎么解释这个呢?
在这里插入图片描述

  删除后是
在这里插入图片描述

  比如上图这棵红黑树的删除过程,要删除1,那么1就是待删除节点,而0就是替换节点,因为0要代替1的位置。最后虽然经过了调整,但确实是0代替了1的位置,成为了2的父亲。
  红黑树无论是删除后调整,还是删除前调整,都是以替换节点来进行判断的。

自己是弟弟 自己是哥哥
兄弟是红节点 case 1
父亲变红
哥哥变黑
左旋
进入侄子全黑场景
case 2
父亲变红
弟弟变黑
右旋
进入侄子全黑场景
兄弟是黑节点小侄子红色 case 3
哥哥变红
小侄子变黑
右旋
变成大侄子红色
case 4
小侄子变黑
弟弟变成父亲的颜色
父亲变黑
右旋
完成调整
大侄子红色 case 5
大侄子变黑
哥哥变成父亲的颜色
父亲变黑
左旋
完成调整
case 6
弟弟变红
大侄子变黑
左旋
变成小侄子红色
侄子全黑色 case 7
兄弟变红,父亲视为新的替换节点

  这就看得让人头疼。总共7种场景。
  case 1,哥哥红色
  case 2,弟弟红色
  case 3,哥哥黑色,小侄子红色
  case 4,弟弟黑色,小侄子红色
  case 5,哥哥黑色,大侄子红色
  case 6,弟弟黑色,大侄子红色
  case 7,侄子全黑色
  场景切换图如下,比较简单:
在这里插入图片描述

场景一 哥哥是红色

在这里插入图片描述

  比如这棵树,要删除5.那么替换节点就是4。用4代替5的位置之后,黑色高度降低了1,那么就必须将4的哥哥黑色高度也降低1,这样才会平衡。
  4的哥哥是7。首先将父亲5变红。这样总体黑色高度都降低1,再将哥哥7变黑,这样哥哥那边黑色高度不变。
在这里插入图片描述
  可以看到变色前,左树高度是3(包含空节点),哥哥黑色高度也是3。
  变色后,弟弟黑色高度是2,哥哥黑色高度是3.
  再左旋
在这里插入图片描述

  左旋之后,弟弟黑色高度为3,哥哥黑色高度为3。
  而要删除的是5,5是红节点,但是这个删除红节点之后还是不会平衡。因为要删除5,必须用4去替换。因为红黑树的性质,如果有两个子节点,其中一个为黑,另一个不能为空。
  为空的是黑色高度1,为黑的,黑色高度肯定大于1。
  现在已经调整到场景7终态-兄弟和侄子全黑。

场景二 弟弟是红色

  测试数据{8, 9, 6, 5, 7, 0, 4, 3, 1, 2},删除6
在这里插入图片描述
  这棵红黑树是黑色高度为4的红黑树,虽然看起来难看,但确实是合法的红黑树。
  将6删除,替换节点是5,弟弟是1,红色。
  变色后
在这里插入图片描述
  右旋后,主要是按4-1-0这条线右旋,如下图所示:
在这里插入图片描述
  经过旋转后,黑色高度还是不变。
  进入场景七-兄弟和侄子全黑。

场景三 哥哥黑色,小侄子红色

  测试数据8, 7, 5, 3, 9, 4, 0, 6, 1, 2,删除0
在这里插入图片描述
  着色后
在这里插入图片描述
  右旋后
在这里插入图片描述
  还是不平衡,还需要继续调整,但是已经进入最后场景五:哥哥黑色,大侄子红色。

场景四 弟弟黑色,小侄子红色(终态)

在这里插入图片描述
  着色后
在这里插入图片描述
  右旋后
在这里插入图片描述
  可以看到左树黑色高度为2,右树黑色高度为2和3。就是有替换节点的地方黑色高度为3,如果将替换节点4删除,那么就完成调整。

场景五 哥哥黑色,大侄子红色(终态)

  测试数据:9, 4, 2, 5, 6, 3, 8, 7, 1, 0,删除5
  删除前
在这里插入图片描述
  这个场景是由别的场景(一般是场景三)调整后得到的。
  调整前:
在这里插入图片描述
  着色后
在这里插入图片描述
  右旋后
在这里插入图片描述
  可以看到要删除的5,就是黑色高度不平衡的那个点,所以可以直接删除。

场景六 弟弟黑色,大侄子红色

  红黑树为在这里插入图片描述
  需要删除的是9
  调整前
在这里插入图片描述  着色后,注意6和7的颜色发生了变化。
在这里插入图片描述
  左旋后
在这里插入图片描述
  变成了场景4的终态。

场景七 侄子全黑(终态)

  测试数据:7, 8, 1, 2, 3, 6, 0, 4, 9, 5,删除2,如下图:
在这里插入图片描述
  因为删除2,所以选择的替换节点是1.着色前:
在这里插入图片描述
  着色后
在这里插入图片描述
  把3变红,那么就是左子树黑色高度2,右子树黑色高度为1.这样就调整为了可删除状态
这时候可能会担心有两个连续的红(2和3),但是因为2要被1替换,所以最后会平衡的。

java代码地址

https://e.coding.net/buildt/data-structure/trees.git

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

醒过来摸鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值