红黑树的删除(下篇)

6 篇文章 0 订阅
3 篇文章 0 订阅

红黑树删除

说到红黑树的删除操作,上篇所讲的 红黑树的插入操作(中篇)。与红黑树插入操作类似,红黑树的删除操作也是通过 重新着色(recoloring) 和 旋转(rotation) 来保证每一次删除操作后依旧满足红黑树的属性的。

在插入操作中,通过判断插入结点 x 的叔叔结点 u 的颜色来确定恰当的平衡操作。而删除操作中,是通过检查兄弟结点的颜色来决定恰当的平衡操作。

红黑树中插入一个结点最容易出现两个连续的红色结点,违背红黑树的性质3(红黑树中不存在两个相邻的红色结点)。而删除操作,最容易造成子树黑高(Black Height)的变化(删除黑色结点可能导致根结点到叶结点黑色结点的数目减少,即黑高降低)。关于黑高不清楚的可以看这篇文章 红黑树的定义(上篇)

与插入操作相比,红黑树的删除操作相对复杂一点,但多点儿耐心,还是没有问题的。为了理解删除操作,我们先来看一个 双黑(Double Black) 的概念。

当删除结点 v 是黑色结点,且其被其黑色子节点替换时,其子结点就被标记为 双黑

在这里插入图片描述在这里插入图片描述
如上图所示,v节点2被左孩子u替代,那么左孩子就变成了双黑节点,在逻辑上是,替换节点继承了被删除节点的颜色,如果替换节点是黑色,被删除节点也是黑色,那么继承之后,替换节点我们定义为双黑,在后续操作上,我们的思路也是将双黑节点的黑色传递出去,最后的效果是不存在双黑节点。

红黑树删除操作

删除操作总体上分为三步:

  1. 执行标准的BST删除操作;

  2. 简单情况:u或者v是红色;

  3. 复杂情况:u和v是黑色。

    3.1. u是双黑节点;
    3.2. 当前节点u是双黑节点并且不是根节点;
    - 3.2.1. u的兄弟节点s 是黑色并且s的孩子节点有一个是红色 (LL,LR,RR,RL)
    - 3.2.2. u的兄弟节点s 是黑色并且s的孩子节点都是黑色
    - 3.3.3. u的兄弟节点是红色 (s是父节点p的左孩子,s是父节点p的右孩子)

    3.3. 当前节点u是双黑节点并且是根节点;

1.标准bst删除操作

在二叉搜索树中删除一个节点,就是先找到指定的节点,然后使用前驱或者后驱的值去覆盖指定的节点,那么删除操作就转变成了对前驱或者后驱节点的删除操作。如下图所示:要删除15节点,用前驱12覆盖15的值,然后前驱12节点需要删除,用12的孩子节点去替换12节点的位置。
在这里插入图片描述在这里插入图片描述

2.u或者v是红色

我们想一想第一步中,标准的bst操作,找到的前驱节点(如果存在的话)最多只有一个非空子节点,要么没有非空子节点。在2情况中,要么被删除的节点是红色,要么有一个子节点是红色,它俩中只有一个是红色节点,这种情况下,只需要删除v节点,替换节点u替代删除节点的位置,将替换节点的颜色设置成黑色即可。v,u中有个红色和一个黑色节点,那么删除红色,保留黑色就可以保持黑高不变的性质。

使用步骤1的红黑树进行操作:

前驱删除法:
在这里插入图片描述在这里插入图片描述

后驱删除法:
在这里插入图片描述在这里插入图片描述

3. u 和 v 都是黑色结点

当 u 和 v 都是黑色结点时,分为三种情况进行处理:

3.1 结点 u 是双黑结点

当删除节点v和替换节点u都是黑色的,只要删除了节点v,那么v节点的颜色黑色会传递给替换节点u,那么u节点变成了双黑节点,接下来的任务是将双黑节点的黑色变成单黑节点,通过旋转、染色操作,将双黑一个黑色传递出去。
中篇中说过,叶子节点的双孩子节点都是黑色的空节点,如果是黑色叶子节点需要删除,那么该节点一定变成双黑节点。

3.2 当前结点 u 是双黑结点且不是根结点

该情况下又分为以下三种情况:

3.2.1. u的兄弟节点s 是黑色并且s的孩子节点有一个是红色 (LL,LR,RR,RL)

双黑节点u的兄弟节点s是黑色,s的子节点中至少有一个是红色节点,这种情况下有四种子情况,分别是LL,LR,RR,RL:
LL(s 是 p 的左孩子,r 是 s 的左孩子,或者 s 的两个孩子都是红色结点):
在这里插入图片描述
删除节点25,产生双黑节点u,那么u的兄弟节点15是黑色,兄弟节点的左子节点10是红色,这种情况就是LL;
那么先r染色为s的颜色,s染色为p的颜色,在对p进行右旋操作,
在这里插入图片描述
在这里插入图片描述
p染黑,u变成单黑节点:
在这里插入图片描述
LR(s 是 p 的左孩子,r 是 s 的右孩子,或者 s 的两个孩子都是红色结点):
删除结点 25 ,不过结点 25 的兄弟结点 15 只有一个右孩子 18 ;
在这里插入图片描述
这种情况可以转化成LL的情况,r节点染色为p节点的颜色,在对s节点进行左旋操作,这个时候红黑树的结构就变成了LL的情况:
在这里插入图片描述

在这里插入图片描述
接下来,操作就很熟悉了,右旋p节点,将p设置为黑色,u节点染成单黑色。
在这里插入图片描述

RR(s 是 p 的右孩子,r 是 s 的右孩子,或者 s 的两个孩子都是红色结点):
删除结点 2 ,用结点 2 的NULL结点 a 替换结点 2 ,产生一个双黑结点 u ,双黑结点 u 的兄弟结点 s 为 15 ,结点 s 是其父结点 6(p) 的右孩子,其右孩子 18(r) 正好是红色结点。即为 RR 情况(仔细观察其实和 LL 情况是对称的)。
在这里插入图片描述
r的颜色变为s的颜色,s的颜色变为p的颜色
在这里插入图片描述
左旋p,p的颜色设置为黑色,双黑变单黑
在这里插入图片描述
RL情况(s 是 p 的右孩子,r 是 s 的左孩子,或者 s 的两个孩子都是红色结点)
该情况与 LR情况是对称的
在这里插入图片描述
结点 r 的颜色变为 p 的颜色
在这里插入图片描述
右旋结点15(s)
在这里插入图片描述
左旋结点6(p),p的颜色设置为黑色,双黑变单黑
在这里插入图片描述

3.2.2. u的兄弟节点s 是黑色并且s的孩子节点都是黑色

对于这种情况需要递归地进行处理,如果删除结点后得到的双黑结点的父结点此时为黑色,则结点 u 变单黑,且结点 u 的父结点 p 变双黑,然后对结点 u 的父结点 p 继续进行处理,直到当前处理的双黑结点的父结点为红色结点,此时将双黑结点的父结点设置为黑色,双黑结点变为单黑结点(红色 + 双黑 = 单黑)。

假设以 6 为根结点的子树为整棵树的左子树,删除结点 9 ,产生双黑结点 u 且其兄弟结点 12(s) 为黑色,兄弟结点的左右孩子均为黑色。
在这里插入图片描述在这里插入图片描述

p节点是黑色的情况,删除节点v后,u变成了双黑节点,将s节点和u的一个黑色向上传递给节点p,那么u变成单黑节点,s变成红色节点,p变成双黑节点(黑 + 黑 = 双黑),以p为新的替换节点u,继续按照上面的 3.u是双黑节点 的条件,进行递归操作。
在这里插入图片描述
p节点是红色的情况,删除节点v后,u变成了双黑节点,将s节点和u的一个黑色向上传递给节点p,那么u变成单黑节点,s变成红色节点,p变成单黑节点(红 + 黑 = 黑),结束操作。
在这里插入图片描述
相对应的还有双黑节点在右边的情况,那么和上面是对称的,操作是对称的,这里不赘述。

3.3.3. u的兄弟节点是红色 (s是父节点p的左孩子,s是父节点p的右孩子)

当前 u 的兄弟结点 s 是红色结点时,通过旋转操作将 u 当前的兄弟结点向上移动,并对 u 的父结点和其旋转前的兄弟结点重新着色,接着继续对结点 u 旋转后的兄弟结点 s 进行判断,确定相应的平衡操作。旋转操作将 u 的兄弟结点情况又会转换为前面刚提到的3.2.2.的情况。根据兄弟结点 s 是父结点 p 的左右孩子又分为两种情况。
在这里插入图片描述在这里插入图片描述
首先,将s节点和父节点p的颜色互换,然后左旋p节点,那么得到下面结果,这样u遇到的情况就是上面3.2.2. 所述的情况(u是双黑节点,可以确定11的子节点也是黑色的,这样两边黑高才是相等的),直接使用3.2.2. 使用的方法进行处理。
在这里插入图片描述在这里插入图片描述
s是父节点p的左孩子的情况,和上述操作对称,这里不赘述。

3.3 当前结点 u 是双黑结点且是根结点

当前结点 u 是双黑结点且是根结点时,直接将双黑结点变为单黑结点,整颗红黑树的黑高减 1.
在这里插入图片描述

红黑树与AVL树的比较

红黑树
红黑树中的每个结点需要一个存储位表示结点的颜色,可以是红或黑。通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保对于每一个结点其到叶子结点的最长路径不会超过最短路径的两倍,因此,红黑树是一种弱平衡二叉树(由于是弱平衡,可以看到,在相同结点的情况下,AVL树的高度<=红黑树),相对于要求严格的AVL树来说,它的旋转次数少,所以对于插入,删除操作较多的情况下,使用红黑树。

红黑树的性质:
每一个结点非红即黑
树根节点是黑色;
所有的NULL结点都是黑色结点;
树中不存在两个相邻的红色结点(即红色结点的父结点和孩子结点均不能是红色);
从任意一个结点(包括根结点)到其任何后代 NULL 结点(默认是黑色的)的每条路径都具有相同数量的黑色结点。

红黑树的应用:
广泛用于C ++的STL中,map 和 set 是用红黑树实现的;
Linux的的进程调度,用红黑树管理进程控制块,进程的虚拟内存空间都存储在一颗红黑树上,每个虚拟内存空间都对应红黑树的一个结点,左指针指向相邻的虚拟内存空间,右指针指向相邻的高地址虚拟内存空间;
IO多路复用的epoll采用红黑树组织管理sockfd,以支持快速的增删改查;
Nginx中用红黑树管理定时器,因为红黑树是有序的,可以很快的得到距离当前最小的定时器;
Java的 TreeMap 和 TreeSet 的实现;

平衡二叉树(AVL树)
AVL树一般用平衡因子判断是否平衡并通过旋转来实现平衡,左右子树树高不超过1,和红黑树相比,AVL树是高度平衡的二叉树,平衡条件必须满足(所有结点的左右子树高度差不超过1)。不管我们是执行插入还是删除操作,只要不满足上面的条件,就要通过旋转来保持平衡,而的由于旋转比较耗时,由此AVL树适合用于插入与删除次数比较少,但查找多的情况。

AVL树的性质:
对每一个结点,其左右子树的高度之差的绝对值小于 2;
当结点的左右子树的高度之差大于等于2时,需要进行平衡操作;
严格平衡,查找速度更快

AVL树的应用:
由于维护这种高度平衡所付出的代价比从中获得的效率收益还大,故而实际的应用不多,更多的地方是用追求局部而不是非常严格整体平衡的红黑树。当然,如果应用场景中对插入删除不频繁,只是对查找要求较高,那么AVL还是较优于红黑树。

Windows NT内核中广泛存在;
数据库查询查询操作较多的情况下;

结尾

参考博客:图解:红黑树删除篇(一文读懂)
源码链接:红黑树实现

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值