红黑树
红黑树里,对任意节点到达任意的叶节点,没有一条路径会比其他任一条路径长出2倍。
红黑树是一种特殊的二叉搜索树,有以下性质:
1. 每个结点或是红色的,或是黑色的。
2. 根结点是黑色的。
3. 每个叶结点是黑色的
4. 如果一个结点是红色的,则它的两个孩子都是黑色的
5. 从任意一个结点出发到叶结点,每条路径的黑高相等。
左旋右旋
左旋:父节点成为左孩子的右节点,原来左孩子的右节点成为父节点的左孩子
右旋:父节点成为右孩子的左节点,原来右孩子的左节点成为父节点的右孩子
LEFT-ROTATE(T, x)
y = x.right //y成为x的右孩子
x.right = y.left //使y的左子树成为x的右子树
if y.left != T.nil
y.left.p = x
y.p = x.p //关联x的父节点给y
if x.p == T.nil
T.root = y
elseif x == x.p.left
x.p.left = y
else
x.p.right = y
y.left = x //x变为y的左孩子
x.p = y
对于旋转前后的两颗树,使用中序遍历将可输出相同的data序列。
插入
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)
如果插入节点的父节点黑色,直接插入。否则,插入节点修正分为以下3中情况(分析情况均以z的父节点是z祖父的左孩子)
情况1:z的叔节点y是红色
将z的父亲与叔节点染成黑色,祖父染成红色,将z赋值为z的祖父,while循环继续
情况2:z的叔节点y是黑色,且z是父节点的右孩子
将z设为z的父节点,对其进行左旋,转换成情况3
情况3:z的叔节点y是黑色,且z是父节点的左孩子
将z的父节点染成黑色,祖父节点染成红色,对祖父节点进行右旋
RB_INSERT_FIXUP(T, z)
while z.p.color == RED
if z.p == z.p.p.left
y = z.p.p.right //y是z的叔结点
if y.color == RED
z.p.color = BLACK //1
y.color = BLACK //1
z.p.p.color = RED //1 保持了红黑树性质5
z = z.p.p //1
elseif z == z.p.right //隐含了y.color == black
z = z.p //2
LEFT_ROTATE(T, z) //2 因为最终要进行右旋,所以让出位置
else //隐含了y.color == black z.p.left
z.p.color = BLACK //3
z.p.p.color = RED //3
RIGHT_ROTATE(T, z.p.p) //3
//z.p是z.p.p的右孩子时,处理与上面相似
else //z.p == z.p.p.right
y = z.p.p.left
if y.color == RED
z.p.color = BLACK //1
y.color = BLACK //1
z.p.p.color = RED //1 保持了红黑树性质5
z = z.p.p //1
elseif z == z.p.left //隐含了y.color == black
z = z.p //2
RIGHT_ROTATE(T, z) //2
else
z.p.color = BLACK //3 增加了 一个黑色
z.p.p.color = RED //3
LEFT_ROTATE(T, z.p.p) //3 保持了性质5
T.root.color = BLACK
删除
与n个结点的红黑树上的其他基本操作相同,删除一个结点要花费O(lg n)时间。
更换子树的方法
RB_TRANSPLANT(T, u, v)
//将v结点替代u结点
if u.p == T.nil
T.root = v
elseif u = u.p.left
u.p.left = v
else
u.p.right = v
v.p = u.p
RB_DELETE(T, z)
y = z
y_original_color = y.color
if z.left == T.nil
x = z.right
RB_TRANSPLANT(T, z, z.right)
elseif z.right == T.nil
x = z.left
RB_TRANSPLANT(T, z, z.left)
else
y = TREE_MAXINUM(z.right)
y_original_color = y.color
x = y.right
if y.p = z
x.p = y
else
RB_TRANSPLANT(T, y, y.right) //equals to RB_TRANSPLANT(T, y, x)
y.right = z.right
y.right.p = y
RB_TRANSPLANT(T, z, y)
y.left = z.left
y.left.p = y
y.color = z.color //z结点的颜色被保留,而后继y的颜色丢弃,这样做减少破坏范围
if y_original_color == BLACK //如果y是黑色,要修正
RB_DELETE_FIXUP(T, x) //x其实是需要保留一重额外黑色的结点
如果y是红色,则
- 根节点仍为黑色
- 不存在两个相邻的红结点
- 性质5不受影 响
如果y是黑色,则:
- 根节点可能变成红色
- x与x.p可能同时为红色
- y所有的祖先都违反了性质5
红黑树的删除就是要设法留住黑点。当被删除的点或其遗孤是红点时不需太多考虑
删除结点后修正分为4种情况(下列分析均以x = x.p.left为前提,相反情况处理相似)
情况1:x的兄弟结点w是红色
断定x的父节点必是黑色,将w染黑色,父节点染红色和对父节点左旋,转变成情况234
情况2:x的兄弟w是黑色,w左右孩子也是黑色
直接将w染成红色,将新的x设定为原x的父节点,该情况是唯一使while循环继续,若是从情况1进入情况2,则下次循环将结束
情况3:x的兄弟w是黑色,w左孩子是红色,右孩子是黑色
将w然红,w的左孩子染黑,对w进行右旋,转换成情况4
情况4:x的兄弟w是黑色,w右孩子是红色。
将w染成父节点的颜色,w的父节点和右孩子染成黑色,对父节点左旋
RB_DELETE_FIXUP(T, x)
//while循环目标是将额外的黑色沿树上移,x指向的节点额外多一重黑色
while x != T.root and x.color == BLACK //若果x.color == RED 那么直接染成黑色
if x == x.p.left
w = x.p.right
if w.color == RED //此时,x与w的parent必定是黑色的,性质4
w.color = BLACK //case1 无论如何,w.color在2,3,4中必定是black
x.p.color = RED //case1
LEFT_ROTATE(T, x.p) //case1
w = x.p.right //case1
if w.left.color == BLACK and w.right.color == BLACK
w.color = RED //case2
x = x.p //case2
elseif w.left.color == RED and w.right.color == BLACK
w.left.color = BLACK //case3
w.color = RED //case3
RIGHT_ROTATE(T, w) //case3
w = x.p.right //case3
else //w.right == RED
w.color = x.p.color //case4
x.p.color = BLACK //case4
w.right.color = BLACK //case4
LEFT_ROTATE(T, x.p) //case4
x = T.root //case4 循环终止
else (same as then clause with "right" and "left" exchanged)
x.color = BLACK