本章介绍二叉搜索树中的红黑树,包括其定义性质、左右旋转、插入和删除操作,重点分析了在红黑树中插入和删除元素的过程,分情况进行详细讨论。
红黑树
红黑树是一种在每个结点上增加了一个存储位表示结点颜色(可以是RED或者BLACK)的二叉搜索树。通过对任何一条从根到叶子的路径上各个着色方式的限制,红黑树确保没有一条路径会比其他路径长出两倍,因而红黑树是一种平衡的二叉查找树。
红黑树的性质如下:
1、每个结点或是红色,或是黑色。
2、根结点是黑色。
3、每个叶子结点(NIL)是黑色。
4、如果有一个结点是红色,则它的两个儿子都是黑色。
5、对每个结点,从该结点到其孙子结点的所有路径上包含相同数目的黑色结点。
如下图为一棵红黑树,其中黑结点涂黑,红结点以浅阴影表示:
引理13.1:一棵有n个内部结点的红黑树的高度至多为2lg(n+1)。
黑高:从某个结点x出发(不包含该结点)到达一个叶子结点的任意一条路径上,黑色结点的个数称为该结点的黑高,记为bh(x)。
旋转
当红黑树进行插入和删除操作使得树的结构形状发生改变而不满足红黑树的某些性质时,就需要通过旋转来保持红黑树的性质,如下图是红黑树的左旋转、右旋转过程:
红黑树的左旋转、右旋转代码实现:
#!/usr/bin/env python
# _*_ coding:utf-8 _*_
class RBTree(object):
def __init__(self):
self.nil = RBTreeNode(0)
self.root = self.nil
class RBTreeNode(object):
def __init__(self, x):
self.key = x
self.left = None
self.right = None
self.parent = None
self.color = 'black'
self.size=None
def LeftRotate( T, x):
y = x.right
x.right = y.left
if y.left != T.nil:
y.left.parent = x
y.parent = x.parent
if x.parent == T.nil:
T.root = y
elif x == x.parent.left:
x.parent.left = y
else:
x.parent.right = y
y.left = x
x.parent = y
def RightRotate( T, x):
y = x.left
x.left = y.right
if y.right != T.nil:
y.right.parent = x
y.parent = x.parent
if x.parent == T.nil:
T.root = y
elif x == x.parent.right:
x.parent.right = y
else:
x.parent.left = y
y.right = x
x.parent = y
插入
红黑树插入新结点的过程是在二叉搜索树插入过程的基础上改进的,先按照二叉排序的插入过程插入到红黑树,然后将新插入的结点标记为红色,之后调用RB_INSERT_FIXUP调整结点并重新着色,使得满足红黑树的性质。
书中分析了当插入新结点后,会破坏红黑树的哪些性质,并针对可能破坏的性质进行分类讨论并给出了解决办法,总共讨论了三种情况,如下图所示:
情况1:z的叔叔结点y是红色的
解决:将z的父结点和叔叔结点y都着为黑色,而将z的祖父结点着为红色,然后从祖父结点继续向上判断是否破坏红黑树的性质
情况2:z的叔叔y是黑色的,而且z是右孩子
情况3:z的叔叔y是黑色的,而且z是左孩子
情况2和情况3中y都是黑色的,通过z是左孩子还是右孩子进行区分,且可以将情况2通过旋转变为情况3。
解决:从z的父结点执行一次左旋转,新的到z成为左子树,变成情况3,之后将z的父结点着还是,祖父结点着红色,再从z的祖父结点处进行一次右旋转。
红黑树的插入代码实现:
#!/usr/bin/env python
# _*_ coding:utf-8 _*_
class RBTree(object):
def __init__(self):
self.nil = RBTreeNode(0)
self.root = self.nil
class RBTreeNode(object):
def __init__(self, x):
self.key = x
self.left = None
self.right = None
self.parent = None
self.color = 'black'
self.size=None
def RBInsert( 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.parent = y
if y == T.nil:
T.root = z
elif z.key < y.key:
y.left = z
else:
y.right = z
z.left = T.nil
z.right = T.nil
z.color = 'red'
RBInsertFixup(T, z)
return z.key, '颜色为', z.color
def RBInsertFixup( T, z):
while z.parent.color == 'red':
if z.parent == z.parent.parent.left:
y = z.parent.parent.right
if y.color == 'red':
z.parent.color = 'black'
y.color = 'black'
z.parent.parent.color = 'red'
z = z.parent.parent
else:
if z == z.parent.right:
z = z.parent
LeftRotate(T, z)
z.parent.color = 'black'
z.parent.parent.color = 'red'
RightRotate(T,z.parent.parent)
else:
y = z.parent.parent.left
if y.color == 'red':
z.parent.color = 'black'
y.color = 'black'
z.parent.parent.color = 'red'
z = z.parent.parent
else:
if z == z.parent.left:
z = z.parent
RightRotate(T, z)
z.parent.color = 'black'
z.parent.parent.color = 'red'
LeftRotate(T, z.parent.parent)
T.root.color = 'black'
def RBTransplant( T, u, v):
if u.parent == T.nil:
T.root = v
elif u == u.parent.left:
u.parent.left = v
else:
u.parent.right = v
v.parent = u.parent
删除
红黑树删除结点过程是在二叉搜索树的删除过程基础上改进的。红黑树在删除结点后仍需检查是否破坏了红黑树的性质,若破坏了红黑树的性质,则需调用RB_DELETE_FIXUP进行调整,且从删除结点y的唯一孩子结点x或者是NIL处开始调整。
书中分析了被删除结点y是黑色会产生的问题,且给出了相应的解决办法。根据被破坏的性质及对应解决方法,书中分为了四种情况进行分析,如下图所示:
情况1:x的兄弟w是红色的
解决:将w着为黑色,x的父结点为红色,再对x父结点做一次左旋转,将情况1转换为情况2、3或4。
情况2:x的兄弟w是黑色的,而且w的两个孩子都是黑色的
解决:从x和w上去掉一重黑色,即x只有一重黑色而w着为红色,给x的父结点添加额外黑色。
情况3:x的兄弟w是黑色的,w的左孩子是红色的,右孩子是黑色的
解决:交换w和其左孩子的颜色,并对w进行右旋转。旋转后x的新兄弟w是一个有红色右孩子的黑结点,转换成了情况4。
情况4:x的兄弟w是黑色的,而且w的右孩子是红色的
将w的颜色设置为x父结点的颜色,将x父结点的颜色设置为黑色,将w的右孩子着为黑色,然后再对x的父结点做一次右旋,最后将x设置为根。
红黑树的删除代码实现:
#!/usr/bin/env python
# _*_ coding:utf-8 _*_
class RBTree(object):
def __init__(self):
self.nil = RBTreeNode(0)
self.root = self.nil
class RBTreeNode(object):
def __init__(self, x):
self.key = x
self.left = None
self.right = None
self.parent = None
self.color = 'black'
self.size=None
def RBDelete(T, z):
y = z
y_original_color = y.color
if z.left == T.nil:
x = z.right
RBTransplant(T, z, z.right)
elif z.right == T.nil:
x = z.left
RBTransplant(T, z, z.left)
else:
y = TreeMinimum(z.right)
y_original_color = y.color
x = y.right
if y.parent == z:
x.parent = y
else:
RBTransplant(T, y, y.right)
y.right = z.right
y.right.parent = y
RBTransplant(T, z, y)
y.left = z.left
y.left.parent = y
y.color = z.color
if y_original_color == 'black':
RBDeleteFixup(T, x)
def RBDeleteFixup( T, x):
while x != T.root and x.color == 'black':
if x == x.parent.left:
w = x.parent.right
if w.color == 'red':
w.color = 'black'
x.parent.color = 'red'
LeftRotate(T, x.parent)
w = x.parent.right
if w.left.color == 'black' and w.right.color == 'black':
w.color = 'red'
x = x.parent
else:
if w.right.color == 'black':
w.left.color = 'black'
w.color = 'red'
RightRotate(T, w)
w = x.parent.right
w.color = x.parent.color
x.parent.color = 'black'
w.right.color = 'black'
LeftRotate(T, x.parent)
x = T.root
else:
w = x.parent.left
if w.color == 'red':
w.color = 'black'
x.parent.color = 'red'
RightRotate(T, x.parent)
w = x.parent.left
if w.right.color == 'black' and w.left.color == 'black':
w.color = 'red'
x = x.parent
else:
if w.left.color == 'black':
w.right.color = 'black'
w.color = 'red'
LeftRotate(T, w)
w = x.parent.left
w.color = x.parent.color
x.parent.color = 'black'
w.left.color = 'black'
RightRotate(T, x.parent)
x = T.root
x.color = 'black'