1.性质
红黑树是一种二叉查找树,但是每个节点增加一个表示结点颜色(红或黑)的字段,并且满足一下条件:
- 每个节点或是红的,或是黑的
- 根节点是黑的
- 每个叶结点(NIL)是黑的
- 如果一个节点是红的,则它的两个儿子都是黑的
- 对每个节点,从该结点到其子孙节点的所有路径上包含相同数目的黑节点
为方便处理边界条件,我们采用一个哨兵来表示NIL,如果某节点没有一个子结点或父结点,则该结点的相应指针指向NIL。
一棵有n个内结点(不包括NIL)的红黑树高度之多为2lg(n+1)。
2.旋转
在红黑树上执行插入或删除操作时,结果都可能违反红黑树的性质,我们需要改变树的指针结构来保持红黑树的性质。旋转是修正过程中会用到的操作,分为左旋和右旋,这里只介绍左旋操作,右旋刚好与左旋操作相反。下面是旋转的示意图
LEFT-ROTATE(T, x) y ← right[x] right[x] ← left[x] if left[y] != nil[T] p[left[y]] ← x p[y] ← p[x] if p[x] = nil[T] root[T] ← y else if x = left[p[x]] left[p[x]] ← y else right[p[x]] ← y left[y] ← x p[x] ← y
3.插入
插入操作与普通二叉树的插入操作接近,将新插入节点设置为红色(尽可能少违反二叉树性质),并在最后增加RB-INSERT-FIXUP(T, z)调节二叉树使其满足红黑树的性质。
RB-INSERT y ← nil[T] x ← root[T] while x != nil[T] do y ← x if key[z] < key[x] x ← left[x] else x ← right[x] p[z] ← y if y = nil[T] root[T] = z else if key[z] < key[y] left[y] ← z else right[y] ← z left[z] ← nil[T] right[z] ← nil[T] color[z] ← RED RB-INSERT-FIXUP(T, z)
RB-INSERT-FIXUP对插入结果进行修饰,可能会存在以下3中违反红黑树性质的情况(示例图假设“当前节点”为x,且父节点是祖父节点的左孩子,如果是右孩子,则将下面左右调转即可):
case 1:父亲是红色的,叔叔也是红色的
处理方法
1.将“父节点”设为黑色
2.将“叔叔节点”设为黑色
3.将“祖父节点”设为红色
4.将“祖父节点”设为当前节点
case 2:父亲是红色的,叔叔是黑色的,当前节点是右孩子
处理方法:
1.将“父节点”作为“当前节点”
2.以“当前节点”为支点进行左旋
case 3:父亲是红色的,叔叔是黑色的,当前节点是左孩
处理方法:
1.将“父节点”设为黑色。
2.将“祖父节点”设为红色。
3.以“祖父节点”为支点进行右旋。
以下是RB-INSERT-FIXUP的过程:
RB-INSERT-FIXUP(T, z) while color[p[z]] = RED // 若“当前节点(z)的父节点是红色”,则进行以下处理。 do if p[z] = left[p[p[z]]] // 若“z的父节点”是“z的祖父节点的左孩子”,则进行以下处理。 y ← right[p[p[z]]] // 将y设置为“z的叔叔节点(z的祖父节点的右孩子)” if color[y] = RED // Case 1条件:叔叔是红色 color[p[z]] ← BLACK ▹ Case 1 // (01) 将“父节点”设为黑色。 color[y] ← BLACK ▹ Case 1 // (02) 将“叔叔节点”设为黑色。 color[p[p[z]]] ← RED ▹ Case 1 // (03) 将“祖父节点”设为“红色”。 z ← p[p[z]] ▹ Case 1 // (04) 将“祖父节点”设为“当前节点”(红色节点) else if z = right[p[z]] // Case 2条件:叔叔是黑色,且当前节点是右孩子 z ← p[z] ▹ Case 2 // (01) 将“父节点”作为“新的当前节点”。 LEFT-ROTATE(T, z) ▹ Case 2 // (02) 以“新的当前节点”为支点进行左旋。 else // Case 3条件:叔叔是黑色,且当前节点是左孩子。 color[p[z]] ← BLACK ▹ Case 3 //(01) 将“父节点”设为“黑色”。 color[p[p[z]]] ← RED ▹ Case 3 // (02) 将“祖父节点”设为“红色”。 RIGHT-ROTATE(T, p[p[z]]) ▹ Case 3 // (03) 以“祖父节点”为支点进行右旋。
// 若“z的父节点”是“z的祖父节点的右孩子”,将上面的操作中“right”和“left”交换位置,然后依次执行。 else (same as then clause with "right" and "left" exchanged) color[root[T]] ← BLACK
4.删除
与插入相似,删除操作也是基本与二叉查找树的删除操作相同,只是在最后要对树做RB-DELETE-FIXUP修复,使得树依然能满足红黑树的性质。
第一步:将红黑树当作二叉查找树,将节点删除,分三种情况
1.被删除节点没有儿子,直接将该节点删除
2.被删除节点只有一个儿子,直接删除该节点,并用该节点的唯一子节点顶替它的位置
3.被删除节点有两个儿子,先找出它的后继节点;然后把“它的后继节点的内容”与“该节点的内容”交换;之后,删除“它的后继节点”。
第二步:对树做RB-DELETE-FIXUP操作,使得树满足红黑树的性质。RB-DELETE-FIXUP操作与RB-INSERT-FIXUP操作类似,不再做解释,以下是删除的过程:
RB-DELETE(T, z) if left[z] = nil[T] or right[z] = nil[T] y ← z // 若“z的左孩子” 或 “z的右孩子”为空,则将“z”赋值给 “y”; else y ← TREE-SUCCESSOR(z) // 否则,将“z的后继节点”赋值给 “y”。 if left[y] ≠ nil[T] x ← left[y] // 若“y的左孩子” 不为空,则将“y的左孩子” 赋值给 “x”; else
x ← right[y] // 否则,“y的右孩子” 赋值给 “x”。 p[x] ← p[y] // 将“y的父节点” 设置为 “x的父节点” if p[y] = nil[T] root[T] ← x // 情况1:若“y的父节点” 为空,则设置“x” 为 “根节点”。 else if y = left[p[y]] left[p[y]] ← x // 情况2:若“y是它父节点的左孩子”,则设置“x” 为 “y的父节点的左孩子” else right[p[y]] ← x // 情况3:若“y是它父节点的右孩子”,则设置“x” 为 “y的父节点的右孩子” if y ≠ z key[z] ← key[y] // 若“y的值” 赋值给 “z”。注意:这里只拷贝z的值给y,而没有拷贝z的颜色 copy y's satellite data into z if color[y] = BLACK then RB-DELETE-FIXUP(T, x) // 若“y为黑节点”,则调用 return y
RB-DELETE-FIXUP(T, x) while x ≠ root[T] and color[x] = BLACK do if x = left[p[x]] w ← right[p[x]] // 若 “x”是“它父节点的左孩子”,则设置 “w”为“x的叔叔”(即x为它父节点的右孩子) if color[w] = RED // Case 1: x是“黑+黑”节点,x的兄弟节点是红色。(此时x的父节点和x的兄弟节点的子节点都是黑节点)。 color[w] ← BLACK ▹ Case 1 // (01) 将x的兄弟节点设为“黑色”。 color[p[x]] ← RED ▹ Case 1 // (02) 将x的父节点设为“红色”。 LEFT-ROTATE(T, p[x]) ▹ Case 1 // (03) 对x的父节点进行左旋。 w ← right[p[x]] ▹ Case 1 // (04) 左旋后,重新设置x的兄弟节点。 if color[left[w]] = BLACK and color[right[w]] = BLACK
// Case 2: x是“黑+黑”节点,x的兄弟节点是黑色,x的兄弟节点的两个孩子都是黑色。 then color[w] ← RED ▹ Case 2 // (01) 将x的兄弟节点设为“红色”。 x ← p[x] ▹ Case 2 // (02) 设置“x的父节点”为“新的x节点”。 else if color[right[w]] = BLACK
// Case 3: x是“黑+黑”节点,x的兄弟节点是黑色;x的兄弟节点的左孩子是红色,右孩子是黑色的。 then color[left[w]] ← BLACK ▹ Case 3 // (01) 将x兄弟节点的左孩子设为“黑色”。 color[w] ← RED ▹ Case 3 // (02) 将x兄弟节点设为“红色”。 RIGHT-ROTATE(T, w) ▹ Case 3 // (03) 对x的兄弟节点进行右旋。 w ← right[p[x]] ▹ Case 3 // (04) 右旋后,重新设置x的兄弟节点。 else // Case 4: x是“黑+黑”节点,x的兄弟节点是黑色;x的兄弟节点的右孩子是红色的。 color[w] ← color[p[x]] ▹ Case 4 // (01) 将x父节点颜色 赋值给 x的兄弟节点。 color[p[x]] ← BLACK ▹ Case 4 // (02) 将x父节点设为“黑色”。 color[right[w]] ← BLACK ▹ Case 4 // (03) 将x兄弟节点的右子节设为“黑色”。 LEFT-ROTATE(T, p[x]) ▹ Case 4 // (04) 对x的父节点进行左旋。 x ← root[T] ▹ Case 4 // (05) 设置“x”为“根节点”。 // 若 “x”是“它父节点的右孩子”,将上面的操作中“right”和“left”交换位置,然后依次执行。 else (same as then clause with "right" and "left" exchanged) color[x] ← BLACK