13.1 红黑树的性质
红黑树是一种二叉树,也是一种平衡搜索树。
红黑树性质:
1、每个节点要么是红色,要么是黑色;
2、根节点是黑色的;
3、每个叶结点(nil)是黑色的;
4、红色节点的两个子节点都是黑色,等价于红色节点父节点是黑色;
5、对每个结点,从该结点到其所有后代叶结点的简单路径上,包含相同数目的黑色结点;
以上的性质保证了红黑树相对平衡,从根节点到所有叶结点的路径中,最长一条最多是最短一条的2倍。(13.1-5)
引理13.1 一棵有n个内部结点的红黑树的高度至多为2lg(n+1).
理解:如上第一副图的红黑树,将所有红色节点吸收到他的黑色父节点中(第二幅图),这样就成了一棵2-3-4Tree,即每个内部结点有2到4个子节点,且每个叶子节点的深度相同,等于原来的黑高。
设原内部结点数为n,叶子节点数则为n+1(如何理解),合并后树高度为h',由于每个内部结点有2-4个子节点,那么叶子节点数介于2^h'和4^h'之间,即2^h'≤n+1≤4^h',得出h'≤lg(n+1).设原红黑树高为h,由于原树最多为红黑相间的,所以h≤2h'≤2lg(n+1).
对红黑树的SEACH、MINIMUM、SUCCESSOR、PREDECESSOR与二叉树相应操作相同,时间是O(lgn).INSERT和DELETE操作与二叉树不同,且较为复杂。
13.2旋转
旋转操作,可保持二叉搜索树的性质。分为:左旋和右旋,如图所示。
右旋:x是y的左孩子,
指针变化:y的父元素成为x的父元素、y成为x的右孩子、β成为y的左孩子。
左旋:y是x的右孩子
指针变化: x的父元素变为y的父元素、x成为y的左孩子、β成为x的右孩子。
伪代码:
LEFT-ROTATE(T,x)
y=x.right;
x.right=y.left; //1、y的左孩子与x的right指针对接
if(y.left!=nil)
y.left.p=x;
y.p=x.p; //2、x的父元素与y父元素对接
if(x.p==nil)
T.root=y;
elseif(x==x.p.left)
x.p.left=y
else x.p.right=y;
y.left=x; //3、x与y的左指针对接
x.p=y;
13.3插入
红黑树的插入操作比二叉树复杂,因为插入后可能会破坏红黑树的5个性质,使其 不再是红黑树。红黑树的RB-INSERT(T,z)在二叉树的TREE-INSERT(T,z)基础上稍微改动,第一、将nil改为T.nil,第二、将z.left、z.right设为T.nil,z颜色设为RED,第三、调用辅助程序RB-INSERT-FIXUP(T,z),调整节点颜色及结构,使其满足红黑树性质。
下面主要看RB-INSERT-FIXUP的工作机制:
插入操作可能破坏的性质为2和4,即根节点必须是黑色和不能有两个红色节点相连。
分两种情况:
A、z的父元素是z的祖父的左孩子
情况A又可分为3种情况:
情况1:如果z节点的叔节点是红色
处理方法:将z的祖父节点的黑色下移至两个子节点,其自身变为红色,这样维持了性质5(路径上黑色结点数没变),z和z.p两个红色不再相连。z上升为自己的祖父结点,进入下一轮循环,经过若干次情况1后,若z为根节点则结束循环,若z的叔节点是黑色,则进入情况2或3
情况2:z的叔节点是黑色且z是z父的右孩子,形象的说是z、z.p、z.p.p成Z形
处理方法:z上升到z.p,左旋z,直接进入情况3.
情况3:z的叔节点是黑色且z是左孩子,z、z.p、z.p.p成线形
处理方法:z.p为黑色,z.p.p为红色,右旋z.p.p
最后,循环结束后,设根节点为黑色。
B、与A对称
分析:总时间是O(lgn),该程序所做的旋转不超过2次,因为只要执行了情况2和3,循环就结束了。
13.4删除
删除操作是整个操作中最复杂的,花费时间仍是O(lgn).可分为两个部分,对节点的删除和对红黑的调整
删除节点:如上图,与二叉树删除大致一样。基本可分为4中情况(z为待删除节点):分别是:1、z.left=T.nil;2、z.right=T.nil;3、z左右孩子都不等于nil,且z的后继等于z的右孩子;4、z左右孩子都不等于nil,且z的后继不等于z的右孩子。
调整红黑:在1、2情况中,y=z,用x替换y;情况3、4中,先x替换y,在y替换z,并且y的颜色取为与z相同,所以z处节点颜色未变。综合4种情况,引起红黑性质破坏的只是x替换y。所以,如果原始y是红色,去掉一个红色节点不会破坏性质。如果y是黑色,x占据y的位置后,包含x的路径上就少了一个黑节点,违返了性质5,我们假想x还有一重黑色,x的color属性还是原色,这样就满足了性质5。然后再想办法把额外的黑色给别人,或者其他操作,满足性质5.
下面具体分析RB-DELETE-FIXUP(T,x),对x的操作过程,
如果x.color=red,即x原色是red,直接将多出的一重黑色给x,就解决了。
如果x=t.root,将x设为黑色,问题解决了。
如果x.color=black,且x不是根节点,进入循环:
分两大种情况A和B(与A对称,只分析A,理解不是很透彻,只简单说一下):
情况1:x兄弟节点是红色:
策略:改变w与x.p的颜色,对x.p左旋,转为情况2、3、4,x没变,仍是双黑;
情况2:x兄弟节点w黑色,且w两个子节点都黑色
策略:由于w子节点都是黑色,可以把w和x额外的黑色上移,转给x.p,并令x为x.p,此时x可能是红黑或双黑,如果前者则退出循环,若后者,继续循环处理;
情况3:w黑色,w左孩子红色,右孩子黑色
交换w和左孩子的颜色,对w进行右旋,转为情况4,x仍是双黑;
情况4:w黑色,w右孩子红色
策略:某些节点颜色修改,并对x.p左旋,从而去掉x的额外黑色。然后将x设置为根,结束循环。