红黑树,看不懂你找我

一:什么是红黑树

红黑树是AVL树的一种流行变种,是具有以下五个特点的二叉查找树

  1. 每一个节点或者着成红色,或者着成黑色
  2. 根是黑色
  3. 每个叶节点Nil是黑色
  4. 如果一个节点是红色的,那么它的子节点必须是黑色的
  5. 每一条从某节点到某Nil指针的路径必须包含相同数目的黑色节点

二:关于红黑树的一般操作

对红黑树操作,最困难的就是保持上述的红黑树性质中的红色标记部分。

1.查找操作

红黑树是二叉查找树,所以其查找操作与一般的二叉查找树相同

  • 令根节点为当前节点
  • 当前节点为Nil,返回Nil
  • 要找的值等于当前节点,返回当前节点
  • 要找的值大于当前节点, 令右儿子为当前节点,重复步骤2
  • 要找的值小于当前节点,令左儿子为当前节点,重复步骤2

操作的时间复杂度O(logN)

2. 插入操作

当我们想向树中插入一个新节点时,通常把这个节点作为叶节点插入。

Part1:
假设把这个点涂成黑色,那完了,肯定违反了条件5,因为本来任意的节点到某Nil指针的路径中黑色节点数是相同的,现在加上黑色节点的这条路径黑色节点数度肯定会+1,于是原来的条件被破坏了,因此,新加的节点必须被涂成红色

Part2:
如果新插入的节点的父节点是黑色的,那么直接插入新的红节点就完事了

Part3:
如果新插入的节点的父节点是红色,那么条件4就被破坏了,这种情况下,我们必须在保持条件5不被破坏的同时,利用改变节点颜色以及树的旋转来满足所有条件。

2.1 新节点:我是红色的,我爹是红色的,我叔叔是黑色的

在这里插入图片描述

  • 设置P为黑色
  • 设置G为红色
  • 进行右单旋转

在这里插入图片描述

  • 设置X为黑色
  • 设置G为红色
  • 进行右双旋转

还有对称的两种情况,操作也是对称的

2.2 新节点:我是红色的,我爹是红色的,我叔叔也是红色的

在这里插入图片描述

  • 把G节点设置为红色
  • 把P节点设置为黑色
  • 把S节点设置为黑色
    在这里插入图片描述
  • 把G节点设置为红色
  • 把P节点设置为黑色
  • 把S节点设置为黑色

这时候,如果G的父节点也是红的,那么G的颜色翻转的操作就会影响性质4,于是我们对G节点以及其父节点GP和父节点的兄弟节点GPS进行2.1所示的旋转操作

注意如果G的父节点GP为红色,G父节点的兄弟节点GPS肯定不能为红色,只能为黑色,因为在红黑树中,不可能出现出现深度小于树的总深度且双兄弟都为红的情况,这种情况在其他的插入或删除过程中已经消除了。
在这里插入图片描述

GP刚好为根结点时,那么根据性质2,我们必须把GP重新设为黑色,那么树的红黑结构变为:黑黑红。换句话说,从根结点到叶子结点的路径中,黑色结点增加了。这也是唯一一种会增加红黑树黑色结点层数的插入情景。

还有对称的两种情况,操作也是对称的

伪代码

RB-INSERT(T, z)
	//红黑树插入
	y = T.nil
	x = T.root
	while x != T.nil
		y = x
		if z.key<x.key
		x = x.left
		else x = x.right
	z.p = y
	if y == T.nil
		T.root = z
	else if z.key < y.key
		y.left = z
	else y.right = z
	z.left = T.nil
	z.right = T.nil
	z.color = RED
	//对红黑树的性质进行恢复
	RB-INSERT-FIXUP(T, z)

RB-INSERT-FIXUP(T, z)
	//插入红色子节点,破坏了性质4时
	while z.p.color = RED
		//父节点为左子树
		if z.p == z.p.p.left
		 	//获取叔叔结点y
			y = z.p.p.right
			//叔叔结点为红色
			if y.color = RED
				z.p.color = BLACK
				y.color = BLACK
				z.p.p.color = RED
				z = z.p.p
			//叔叔结点为黑色
			else 
				//新插入的结点为父节点的右孩子,先来个父子调换,先进行一次左旋转
				if z == z.p.right
					z = z.p
					LEFT-ROTATE(T,z)
				//化成了叔叔结点黑色的一般情况
				z.p.color  = BLACK
				z.p.p.color = RED
				RIGHT-ROTATE(T, z.p.p)
		//对称情况
		else (same as then clause with "right" and "left" exchanged)
	T.root.color = BLACK			

难点来了,难点来了!!


其实不难

3.删除操作

删除操作一直一来是树这种ADT的难点所在

一般二叉搜索树的删除操作:

  • 该节点为叶节点的情况:可以直接删除,将其父节点的儿子指针设为Nil
  • 该节点只有一个非空子节点:可以直接删除,将其父节点的儿子指针指向其唯一儿子
  • 该节点有两个非空子节点:用该节点左子树的最大节点或者右子树的最小节点替换该节点,然后删除对应的左子树最大节点或者右子树最小节点,最终可以回到前两种情况。

好,到这里你肯定还是明白的对吧

现在考虑红黑树的删除操作。

注意:
根据一般二叉搜索树的删除操作来看,我们要删除的节点其实不一定是最终从图中移出去的节点,称最终从图中移出去的节点为替代点,我们先从右子树的最小节点(或左子树的最大节点)找到我们的替代点,与删除点的值交换,但是颜色都不变,然后再删除替代点,然后对树的性质进行恢复。而现在这些替代点都是一些树的末节点(区别于叶节点,我们现在将Nil节点视为黑色叶节点),删除操作便会简化许多,并可以归纳为像插入那样的几个类别。当然了替代点和我们要删除的点也有可能是同一点,但处理方法是一样的。

我们虽然删除了替代点,但是我们对树进行恢复时,还是会将该替代点放在树中参与恢复,恢复完成后再移掉。

在本文中,示例都默认是选择右子树的最小节点作为真正删除点,也可以选择左子树的最大节点

在这里插入图片描述

3.1替代点是红色的

直接从树中去掉即可,不会影响红黑树的性质,算法直接结束

不会出现替代点为红色且有儿子为非叶子节点的情况,所以这种情况是最简单的
在这里插入图片描述

3.2替代点是黑色的

替代点是黑色,那么删除之后树的性质就被破坏了,需要进行修复

3.2.1 有一个儿子为且必须为红色时

当替代节点只有一个儿子且为红色时
直接用它的红色儿子节点取代它,并将颜色改为黑色,这样所有经过替代节点的路径都将经过他的儿子,黑色高度不变。

在这里插入图片描述
注意这跟上面图不一样,这张图D节点是替代节点

不可能存在只有一个儿子且儿子为黑色的情况,那样本身就是不平衡的

3.2.2 有两个黑色儿子

当替代点是黑色的且有两个黑色儿子(可以为叶节点Nil)时,那他肯定有兄弟,那么又可以分为其他几类

  • 替代点是黑色,其兄弟节点是红色
  • 替代点是黑色,其兄弟节点是黑色
3.2.2.1 替代点的兄弟节点是红色

在这里插入图片描述

  • 将G节点也就是替代点的父节点设置为红色,
  • 兄弟节点S设置为黑色
  • 然后进行向左单旋转,

现在可以直接删掉D了吗?(以下都默认标识D为替代点) 不 行 , t a n 90 ° \hspace4ex 不行,tan90\degree tan90°

这个操作不改变任何路径的黑色高度,这时候删去D肯定会变的不平衡,我们只是把情况变为了兄弟节点为黑色的情况,也就是下面将要叙述的情况,我们接下来需要重新进入算法,进一步处理

3.2.2.2 替代点的兄弟节点是黑色

替代点D与其兄弟节点S都是黑色的,所以D的父节点颜色是不确定的,用绿色表示

又分为以下几种情况

3.2.2.2.1 当兄弟节点的两个儿子也都是黑色

在这里插入图片描述
PS:C1、C2可为Nil

  • S节点颜色变为红
  • G节点颜色变黑
  • 把G作为新的替代点进行下一轮操作

假设G一开始是黑色的,这个操作过后,所有一开始经过S节点的路径黑色高度-1,那么在删除D节点后,所有经过D节点的路径黑色高度也会-1,这棵子树大家黑色高度都减一,结局竟该死的甜美,但是经过G的比不经过G的黑色高度减一了,所以再在G上重新平衡处理

假设G一开始是红色的,这个操作后,所有一开始经过S节点的路径黑色高度不变,经过D的路径黑色高度+1,那么在删除D后,所有经过D节点的路径黑色高度又变回来了,结局竟还是该死的甜美,经过G为根的子树已经平衡了,再在G上重新平衡处理

注意当G是树的根时,就表示我们已经做完了,我们从所有路径去除了一个黑色节点,所有性质在上溯过程中都保持着。

3.2.2.2.2 当兄弟节点的左儿子是红色的,右儿子是黑色

在这里插入图片描述

  • C1设置为黑色
  • S设置为红色
  • 对S进行向右单旋转

这样的操作不改变任何路径的黑色高度,本来的平衡状态不变,所以删除D之后就破坏了原先的条件,还要进行自平衡变换,此时成了下面一种情况继续处理

3.2.2.2.3 当兄弟节点的右儿子是红色的,左儿子随意

在这里插入图片描述

  • 交换G和S的颜色
  • 设置兄弟节点的右儿子C2为黑色
  • 进行向左单旋转

该操作使所有变化前经过S节点的路径黑色高度都不变,经过D的路径黑色高度+1,在删除D后,所有经过G节点的路径都不变,达到平衡

说白了删除就是不断递归变换直到满足替代点的删除条件。

例子:删除30根节点
在这里插入图片描述

在这里插入图片描述
练习一下吧,这里有个在线红黑树工具,戳

三:渐进边界的证明

包含n个内部节点的红黑树的高度是 O ( l o g n ) O(logn) O(logn)

定义:

h ( v ) 表 示 以 节 点 v 为 根 的 子 树 的 高 度 。 {\displaystyle h(v)}表示以节点{\displaystyle v}为根的子树的高度。 h(v)v
b h ( v ) 表 示 从 v 到 子 树 中 任 何 叶 子 的 黑 色 节 点 的 数 目 {\displaystyle bh(v)}表示从{\displaystyle v}到子树中任何叶子的黑色节点的数目 bh(v)v
( 如 果 v 是 黑 色 则 不 计 数 它 , 也 叫 做 黑 色 高 度 ) 。 (如果{\displaystyle v}是黑色则不计数它,也叫做黑色高度)。 v

引 理 : 以 节 点 v 为 根 的 子 树 有 至 少 2 b h ( v ) − 1 个 内 部 节 点 。 引理:以节点{\displaystyle v}为根的子树有至少2^{bh(v)}-1个内部节点。 v2bh(v)1
引 理 的 证 明 ( 通 过 归 纳 高 度 ) : 引理的证明(通过归纳高度):

归 纳 假 设 : 归纳假设:
如 果 v 的 高 度 是 零 则 它 必 定 是 N I L , 因 此 b h ( v ) = 0 b h ( v ) = 0 。 所 以 : 2 b h ( v ) − 1 = 2 0 − 1 = 0 如果v的高度是零则它必定是NIL,因此{\displaystyle bh(v)=0}{\displaystyle bh(v)=0}。所以:2^{{bh(v)}}-1=2^{{0}}-1=0 vNILbh(v)=0bh(v)=02bh(v)1=201=0

如 果 h ( v ) = k , 有 2 b h ( v ) − 1 个 内 部 节 点 成 立 如果h(v)=k,有2^{bh(v)}-1个内部节点成立 h(v)=k2bh(v)1
于 是 h ( v ′ ) = k + 1 于是h(v')=k+1 h(v)=k+1
因 为 v ′ 有 h ( v ′ ) > 0 所 以 它 是 个 内 部 节 点 。 同 样 的 它 有 的 两 个 儿 子 , 其 黑 色 高 度 要 么 是 b h ( v ′ ) 要 么 是 b h ( v ′ ) − 1 ( 依 据 v ′ 是 红 色 还 是 黑 色 ) 。 通 过 归 纳 假 设 每 个 儿 子 都 有 至 少 2 b h ( v ′ ) − 1 − 1 个 内 部 接 点 , 所 以 v ′ 有 : 因为v'有h(v')>0 \\所以它是个内部节点。同样的它有的两个儿子,其黑色高度要么是bh(v')要么是bh(v')-1(依据v'是红色还是黑色)。通过归纳假设每个儿子都有至少2^{{bh(v')-1}}-1个内部接点,所以v'有: vh(v)>0bh(v)bh(v)1v2bh(v)11v
2 b h ( v ′ ) − 1 − 1 + 2 b h ( v ′ ) − 1 − 1 + 1 = 2 b h ( v ′ ) − 1 个 内 部 节 点 。 2^{bh(v')-1}-1+2^{bh(v')-1}-1+1=2^{bh(v')}-1个内部节点。 2bh(v)11+2bh(v)11+1=2bh(v)1

使 用 这 个 引 理 我 们 现 在 可 以 展 示 出 树 的 高 度 是 对 数 性 的 。 因 为 在 从 根 到 叶 子 的 任 何 路 径 上 至 少 有 一 半 的 节 点 是 黑 色 ( 根 据 红 黑 树 性 质 4 ) , 根 的 黑 色 高 度 至 少 是 h ( r o o t ) 2 通 过 引 理 我 们 得 到 : 使用这个引理我们现在可以展示出树的高度是对数性的。\\ 因为在从根到叶子的任何路径上至少有一半的节点是黑色(根据红黑树性质4),\\根的黑色高度至少是 {\frac {h(root)}{2}}通过引理我们得到: 使(4)2h(root)

n ⩾ 2 h ( r o o t ) 2 − 1 ↔    log ⁡ ( n + 1 ) ⩾ h ( r o o t ) 2 ↔    h ( r o o t ) ⩽ 2 log ⁡ ( n + 1 ) n ⩾ 2 h ( r o o t ) 2 − 1 ↔    log ⁡ ( n + 1 ) ⩾ h ( r o o t ) 2 ↔    h ( r o o t ) ⩽ 2 log ⁡ ( n + 1 ) {\displaystyle n\geqslant 2^{\frac {h(root)}{2}}-1\leftrightarrow \;\log {(n+1)}\geqslant {\frac {h(root)}{2}}\leftrightarrow \;h(root)\leqslant 2\log {(n+1)}}n\geqslant 2^{{{\frac {h(root)}{2}}}}-1\leftrightarrow \;\log {(n+1)}\geqslant {\frac {h(root)}{2}}\leftrightarrow \;h(root)\leqslant 2\log {(n+1)} n22h(root)1log(n+1)2h(root)h(root)2log(n+1)n22h(root)1log(n+1)2h(root)h(root)2log(n+1)

因 此 根 的 高 度 是 O ( log ⁡ n ) 因此根的高度是{\displaystyle {\text{O}}(\log n)} O(logn)

在这里插入图片描述

  • 6
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Hack Rabbit

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

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

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

打赏作者

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

抵扣说明:

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

余额充值