第13章 红黑树
最后更新于:2022-04-01 07:35:38
# 一、概念
### 1.定义与性质
(1)定义
红黑树字义:满足(a)每个结点或是红的,或是黑的(b)根结点是黑的(c)每个叶结点(NIL)是黑的(d)如果一个结点是红的,则它的两个儿子是黑的(e)对每个结点,从该结点到其子孙结点的所有路径上包含相同数目的黑结点的二叉查找树称为红黑树。
黑高度定义:从某个结点x出发(不包括该结点)到达一个叶结点的任意一条路径上的黑色结点的个数称为x的黑高度。
(2)性质
红黑树确保没有一条路径会比其它路径长出两倍。
一棵有n个内结点的红黑树的高度至多为2lg(n+1)
### 2.结构
(1)红黑树结点结构
~~~
struct node
{
node *left;
node *right;
node *p;
int key;
bool color;
};
~~~
(2)红黑树结构
~~~
struct Red_Black_Tree
{
node *root;//根结点
node *nil;//哨兵
};
~~~
### 3.红黑树上的操作
SEARCH
PREDECESSOR
MINIMUM
MAXIMUM
INSERT
DELETE
# 二、代码
[头文件](https://code.csdn.net/mishifangxiangdefeng/exerciseforalgorithmsecond/tree/master/include/chapter13/redBlackTree.h)
[产品代码](https://code.csdn.net/mishifangxiangdefeng/exerciseforalgorithmsecond/tree/master/src/chapter13/redBlackTree.cpp)
[测试代码](https://code.csdn.net/mishifangxiangdefeng/exerciseforalgorithmsecond/tree/master/tst/chapter13/redBlackTreeTest.cpp)
代码说明:
1.使用NIL作为叶子结点,代替NULL,这样可以少一些特殊处理
2.删除一个结点,delete改为remove,一方面是避免与关键字冲突,另一方面,觉得remove语义更符合
3.insert和remove使用int key代替node *z作为参数,觉得这样使用更方便
# 三、练习
### 13.1 红黑树的性质
13.1-1
黑高度是指从根结点到叶结点的路径上黑色结点的个数,需要注意的是,计算黑高度时,出发的结点不计算在内,叶结点是指nil结点
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-02_56b02bd0e5c3a.gif)
13.1-2
不是,因为违反性质4
不是,因为违反性质5
13.1-3
是
13.1-4
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-02_56b02bd100e8d.gif)
[http://zh.clrs-ans.wikia.com/index.php?title=13.1_%E7%BA%A2%E9%BB%91%E6%A0%91%E7%9A%84%E6%80%A7%E8%B4%A8&variant=zh-cn](http://zh.clrs-ans.wikia.com/index.php?title=13.1_%E7%BA%A2%E9%BB%91%E6%A0%91%E7%9A%84%E6%80%A7%E8%B4%A8&variant=zh-cn)
叶子深度为黑高度
13.1-5
bh(x)为黑高度,相应的定义rh(x)为红高度,根据RB树性质
rh(x)<=bh(x),而每条路径的bh(x)都想等,最长可能路径bh(x)+rh(x),最短可能路径bh(x),故最长是最短的至多两倍。
13.1-6
最多:2^(2k+1)-1
高少:2^(k)-1
13.1-7
比值最大为2:1,奇数层的结点为红色,偶数层的结点为黑色。n为奇数。
比值最小为0,全部结点都为黑色
### 13.2 旋转
~~~
13.2-1,代码见上文
RIGHT-ROTATE
1 y <- left[x]
2 left[x] <- right[y]
3 if right[y] != nil[T]
4 then p[right[y]] <- x
5 p[y] <- p[x]
6 if p[x] = nil[T]
7 then root[T] <- y
8 else if x = right[p[x]]
9 then right[p[x]] <- y
10 else left[p[x]] <- y
11 right[y] <- x
12 p[x] <- y
13.2-3
a的深度+1,b的深度不变,c的深度-1
~~~
### 13.3 插入
13.3-1
1楼说得很好
~~~
不是因为违反了性质5才不会采用把节点标志位黑色。如果只是因为违反了性质5,完全可以调整,就跟RB-INSERT中将插入的节点标志位红色违反了性质4或者2一样,都是可以通过跳整来符合要求。其原因是插入节点共有六种情况(通过RB-INSERT-FIXUP中的判断语句可以看出:插入节点z的父节点为左孩子有三种,为右孩子也有三种。举父节点为左孩子为例:第一种为如果z的叔叔为红是第一种情况,如果叔叔为黑且z为右孩子为第二种情况,如果叔叔为黑且z为左孩子为第三种情况),前三种情况中只有第二种z最终为黑色节点,其余两种z最终为红色。z最终为红色的比例较高,(4:2),所以一开始将z定义为红色,最后不用修改的概率较高,这是考虑到代码的优化而不是因为违反了性质5。
~~~
13.3-2
运行以上程序能得到结果
13.3-6
见[算法导论-13.3-6](http://blog.csdn.net/mishifangxiangdefeng/article/details/7727271)
令待插入的元素是z。在插入的过程记录从根结点到z的路径,并用栈存储。那么z的父结点就是栈顶元素,z的祖父结点就是栈的次顶元素。
在向上迭代的过程同时出栈,控制好出栈的时间,就能正确实现RB-INSERT
### 13.4 删除
~~~
13.4-3
运行以上程序能得到结点
13.4-4
(1)某黑色结点被删除后,第一个被调整的点是被删除结点的孩子(这个被删除的结点最多只有一个孩子),若这个被删除结点没有孩子,那么第一个被高速的点是nil
(2)x的兄弟w没有孩子,或左旋时w没有左孩子,或右旋时w没有右孩子
13.4-7
可能不相同。
(1)新加入结点后可能会导致某些旋转操作,删除后不会转回来
(2)即使树的结构不变,结点的颜色也有可能会改变
~~~
# 四、思考题
### 13-1 动态持久集合
见[算法导论-13-1-持久动态集合](http://blog.csdn.net/mishifangxiangdefeng/article/details/7903794)
### 13-2 红黑树上的连接操作
见[算法导论-13-2-红黑树上的连接操作](http://blog.csdn.net/mishifangxiangdefeng/article/details/7904581)