数据结构之红黑树
此文是数据结构与算法之美学习笔记
二叉查找树在频繁的动态更新的过程中,可能会出现树的高度很大的情况,从而导致各个操作的效率下降,极端情况下,二叉树会退化为链表,为了解决这种复杂度退化的问题,需要设计一个平衡的二叉查找树。
平衡二叉查找树
平衡二叉查找树的定义是,二叉树中的任意一个节点的左右子树的高度相差不能大于1。完全二叉树和满二叉树都是平衡二叉树的一种。
平衡的意思就是让树的左右看起来比较对称,不要出现左子树很高,右子树很低的情况,这样就能让整棵树的高度尽可能的低一些,相对应的插入,删除,查找等操作效率也就高一些。
红黑树
红黑树英文名:Red-Black Tree 简称R-B Tree。是一种不严格的平衡二叉查找树。
红黑树上的节点,一类被标记为黑色,一类被标记为红色,一般有一下特性:
- 每个节点是黑色或者红色
- 跟节点是黑色的
- 每个叶子节点都是黑色的空节点(NULL)不存数据
- 任何相邻的节点都不能同时为红色,红色节点是被黑色节点隔开的
- 每个节点从该节点达到其叶子节点的所有路径
红黑树的操作
在插入和删除结点的时候,上面的第三四五点就会被破坏,就不是一颗红黑树了,通过旋转可以让它重新成为红黑树。旋转的目的就是为了保持红黑树的特性。
旋转包括左旋,右旋,变色。
如图:
左旋意味着把x结点变成一个左结点,它的左孩子变成它的父节点
右旋意味着把x结点变成一个右结点,它的右孩子变成它的父节点
变色就是节点的颜色由红变黑或者由黑变红
插入操作:
红黑树规定,插入的结点必须是红色的,而且二叉查找树中新插入的结点都是放在叶子结点上。
- 如果插入的结点的父节点是黑色的,那么我们什么都不用做
- 如果插入的结点是跟结点,那么直接改变它的颜色为黑色就可以
- 如果不是以上两种情况,需要左右旋转来改变颜色。
红黑树的平衡调整过程是一个迭代的过程,我们把正在处理的结点叫做关注结点,关注结点会随着不停的迭代而发生变化,最开始的关注结点就是新插入的结点。
新插入结点之后,如果红黑树的平衡被打破,一般有三种情况,我们需要根据每种情况的特点迭代处理,不停的调整
(1)当前结点(被插入的结点)的父节点是红色,并且当前结点的叔叔结点也是红色
- 把父节点和叔叔结点设为黑色
- 把祖父结点设为红色
- 把祖父结点视为当前结点,然后继续对当前结点进行下面的2或者3操作
(2)叔叔结点是黑色,当前结点是其父节点的右子结点
- 把父节点当成新的当前结点
- 以当前结点为支点左旋
(3)叔叔是黑色,当前结点是左结点
- 把父节点设置黑色
- 把祖父结点设为红色
- 以祖父结点为支点右旋
删除操作
删除操作的平衡调整分成两部分
第一步:把它当成一个普通的二叉查找树,删除节点,和正常的二叉查找树一样的操作
1)被删除的节点是叶子节点,直接删除
2被删除的节点只有一个子节点,删除此节点,让其子节点顶替它的位置
3)被删除的节点有两个子节点,先找到这个节点的右子树中的最小节点,把它的内容复制到要删除的节点上,然后在删除掉这个最小节点,对于最小节点的删除可以使用上面的两个规则删除
第二步:删除之后可能会违背红黑树的特性了,通过旋转和重新着色来修整它
开篇的时候说了红黑树的五个特性,如果一个节点删除了,就可能违反红黑树的2,4,5这三个特性需要去解决。
假如我们删除了一个黑色节点X,那么就少了一个黑色节点,跟第5个特性不符合,我们可以假设在删除的位置增加一个额外的黑色就能弥补失去的黑色了。这时候X节点就包含两种颜色,红黑或者黑黑,这时候又违背了特性1。这时候我们就从解决2,4,5三个问题变成了解决1,2,4三个问题。
解决1,2,4的问题,核心是把X节点所包含的黑色往根节点移动。
- 如果X是一个红黑节点,就把X设置成黑色
- 如果X是黑黑节点并且指是根节点,啥也不用做
- 如果X是黑黑节点,并且不是根节点有四种子情况
3.1 X是黑黑节点,X的兄弟节点是红色(这时候X的父节点和X的兄弟节点的子节点都是黑色)
<1>围绕其父节点左旋
<2>X的父节点和祖父节点交换颜色(X的父节点设为红色,祖父节点设为黑色)
<3>继续从4种情况中找出合适的规则调整
3.2 X是黑黑节点,X的兄弟节点是黑色,X的兄弟节点的两个孩子都是黑色
<1> 把X的兄弟节点变成红色
<2> 把X的一个黑移到其父节点上
<3> 把X的父节点设置为新的X节点
<4> 继续从4种情况中找出合适的规则调整
3.3 X是黑黑节点,X的兄弟节点是黑色,X的兄弟节点的左子树是红色,右子树是黑色
<1> 围绕X节点的兄弟节点右旋
<2> X的新兄弟节点和其右子节点交换颜色
<3> 继续从4种情况中找出合适的规则调整
3.4 X是黑黑节点,X的兄弟节点是黑色,X的兄弟节点的右孩子是红色,X的兄弟节点的左孩子是任意颜色
<1> 把X的父节点颜色赋值给X的兄弟节点
<2> 把X父节点设置为黑色
<3> 把X兄弟节点的右子节点设置为黑色
<4> 围绕X的父节点左旋
<5> X中去掉一个黑色