红黑树是基于二叉排序树的升级版,具有以下特性(自平衡特性):
1:节点非红色即黑色
2:根节点为黑色的
3:叶子结点都是黑色的空结点(nil结点)
4:每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)(就是红色后要接一个黑色的,黑色后一般也要接一个红色的)
5:从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点(左子树和右子树的黑结点的层数是相等)
红黑树从根节点到叶子节点的最长路径不会超过最短路径的两倍。
插入的节点都是红色的节点
红黑树的基本操作是添加、删除。在对红黑树进行添加或删除之后,都会用到旋转方法。为什么呢?道理很简单,添加或删除红黑树中的节点之后,红黑树就发生了变化,可能不满足红黑树的5条性质,也就不再是一颗红黑树了,而是一颗普通的树。而通过旋转,可以使这颗树重新成为红黑树。简单点说,旋转的目的是让树保持红黑树的特性。 旋转包括两种:左旋 和 右旋。
左旋:(以x为节点进行左旋)
对x进行左旋,意味着,将“x的右孩子”设为“x的父亲节点”;即,将 x变成了一个左节点(x成了为z的左孩子)!。 因此,左旋中的“左”,意味着“被旋转的节点将变成一个左节点”。
右旋:(以x为节点进行右旋)
对x进行右旋,意味着,将“x的左孩子”设为“x的父亲节点”;即,将 x变成了一个右节点(x成了为y的右孩子)! 因此,右旋中的“右”,意味着“被旋转的节点将变成一个右节点”。
1.1 左旋伪代码
LEFT-ROTATE(T, x)
01 y ← right[x]
// 前提:这里假设x的右孩子为y。下面开始正式操作
02 right[x] ← left[y]
// 将 “y的左孩子” 设为 “x的右孩子”,即 将β设为x的右孩子
03 p[left[y]] ← x
// 将 “x” 设为 “y的左孩子的父亲”,即 将β的父亲设为x
04 p[y] ← p[x]
// 将 “x的父亲” 设为 “y的父亲”
05 if p[x] = nil[T]
06 then root[T] ← y
// 情况1:如果 “x的父亲” 是空节点,则将y设为根节点
07 else if x = left[p[x]]
08 then left[p[x]] ← y
// 情况2:如果 x是它父节点的左孩子,则将y设为“x的父节点的左孩子”
09 else right[p[x]] ← y
// 情况3:(x是它父节点的右孩子) 将y设为“x的父节点的右孩子”
10 left[y] ← x // 将 “x” 设为 “y的左孩子”
11 p[x] ← y // 将 “x的父节点” 设为 “y”
1.2 右旋伪代码
RB-INSERT(T, z)
01 y ← nil[T] // 新建节点“y”,将y设为空节点。
02 x ← root[T] // 设“红黑树T”的根节点为“x”
03 while x ≠ nil[T] // 找出要插入的节点“z”在二叉树T中的位置“y”
04 do y ← x
05 if key[z] < key[x]
06 then x ← left[x]
07 else x ← right[x]
08 p[z] ← y // 设置 “z的父亲” 为 “y”
09 if y = nil[T]
10 then root[T] ← z
// 情况1:若y是空节点,则将z设为根
11 else if key[z] < key[y]
12 then left[y] ← z
// 情况2:若“z所包含的值” < “y所包含的值”,则将z设为“y的左孩子”
13 else right[y] ← z
// 情况3:(“z所包含的值” >= “y所包含的值”)将z设为“y的右孩子”
14 left[z] ← nil[T] // z的左孩子设为空
15 right[z] ← nil[T]
// z的右孩子设为空。至此,已经完成将“节点z插入到二叉树”中了。
16 color[z] ← RED // 将z着色为“红色”
17 RB-INSERT-FIXUP(T, z)
// 通过RB-INSERT-FIXUP对红黑树的节点进行颜色修改以及旋转,让树T仍然是一颗红黑树
2 插入伪代码
RB-INSERT(T, z)
01 y ← nil[T] // 新建节点“y”,将y设为空节点。
02 x ← root[T] // 设“红黑树T”的根节点为“x”
03 while x ≠ nil[T]
// 找出要插入的节点“z”在二叉树T中的位置“y”
04 do y ← x
05 if key[z] < key[x]
06 then x ← left[x]
07 else x ← right[x]
08 p[z] ← y // 设置 “z的父亲” 为 “y”
09 if y = nil[T]
10 then root[T] ← z // 情况1:若y是空节点,则将z设为根
11 else if key[z] < key[y]
12 then left[y] ← z
// 情况2:若“z所包含的值” < “y所包含的值”,则将z设为“y的左孩子”
13 else right[y] ← z
// 情况3:(“z所包含的值” >= “y所包含的值”)将z设为“y的右孩子”
14 left[z] ← nil[T] // z的左孩子设为空
15 right[z] ← nil[T]
// z的右孩子设为空。至此,已经完成将“节点z插入到二叉树”中了。
16 color[z] ← RED // 将z着色为“红色”
17 RB-INSERT-FIXUP(T, z)
// 通过RB-INSERT-FIXUP对红黑树的节点进行颜色修改以及旋转,让树T仍然是一颗红黑树
3 删除伪代码
RB-INSERT-FIXUP(T, z)
01 while color[p[z]] = RED
// 若“当前节点(z)的父节点是红色”,则进行以下处理。
02 do if p[z] = left[p[p[z]]]
// 若“z的父节点”是“z的祖父节点的左孩子”,则进行以下处理。
03 then y ← right[p[p[z]]]
// 将y设置为“z的叔叔节点(z的祖父节点的右孩子)”
04 if color[y] = RED
05 then color[p[z]] ← BLACK
06 color[y] ← BLACK
07 color[p[p[z]]] ← RED
08 z ← p[p[z]]
09 else if z = right[p[z]]
10 then z ← p[z]
11 LEFT-ROTATE(T, z)
12 color[p[z]] ← BLACK
13 color[p[p[z]]] ← RED
14 RIGHT-ROTATE(T, p[p[z]])
15 else (same as then clause with "right" and "left" exchanged)
16 color[root[T]] ← BLACK