红黑树

来源:互联网 发布:bp神经网络算法 编辑:程序博客网 时间:2024/06/01 22:37

红黑树

红黑树,一种二叉查找树,但在每个节点上增加一个存储为表示节点的颜色,可以试Red或Black。通过对任何一条从根节点到叶子的路径上各个节点找色方式的限制,红黑树确保没有一条路径会比其它路径长出2倍,因而是接近平衡的。

二叉查找树

红黑树的5条性质

  1. 每个节点非红即黑
  2. 根节点是黑色
  3. 每个叶节点(NULL节点)都是黑色
  4. 如果一个节点是红色,那么它的两个儿子都是黑色
  5. 对任意节点而言,其到叶节点NULL指针的路径都包含相同数目的黑节点。

如下图所示:

红黑树

红黑树旋转

当对红黑树进行了插入和删除操作,有可能会破坏红黑树的性质,因此需要调整数的结构,改变某些节点的颜色以及进行旋转操作,以使得红黑树继续维持它的5条性质。

左旋

左旋

LEFT-ROTATE(T, x)   y ← right[x]            // 前提:这里假设x的右孩子为y。下面开始正式操作 right[x] ← left[y]      // 将 “y的左孩子” 设为 “x的右孩子”,即 将β设为x的右孩子 p[left[y]] ← x          // 将 “x” 设为 “y的左孩子的父亲”,即 将β的父亲设为x p[y] ← p[x]             // 将 “x的父亲” 设为 “y的父亲” if p[x] = nil[T]        then root[T] ← y                 // 情况1:如果 “x的父亲” 是空节点,则将y设为根节点 else if x = left[p[x]]             then left[p[x]] ← y    // 情况2:如果 x是它父节点的左孩子,则将y设为“x的父节点的左孩子”           else right[p[x]] ← y   // 情况3:(x是它父节点的右孩子) 将y设为“x的父节点的右孩子” left[y] ← x             // 将 “x” 设为 “y的左孩子” p[x] ← y                // 将 “x的父节点” 设为 “y”

右旋

右旋

RIGHT-ROTATE(T, y)   x ← left[y]             // 前提:这里假设y的左孩子为x。下面开始正式操作 left[y] ← right[x]      // 将 “x的右孩子” 设为 “y的左孩子”,即 将β设为y的左孩子 p[right[x]] ← y         // 将 “y” 设为 “x的右孩子的父亲”,即 将β的父亲设为y p[x] ← p[y]             // 将 “y的父亲” 设为 “x的父亲” if p[y] = nil[T]        then root[T] ← x                 // 情况1:如果 “y的父亲” 是空节点,则将x设为根节点 else if y = right[p[y]]             then right[p[y]] ← x   // 情况2:如果 y是它父节点的右孩子,则将x设为“y的父节点的左孩子”           else left[p[y]] ← x    // 情况3:(y是它父节点的左孩子) 将x设为“y的父节点的左孩子” right[x] ← y            // 将 “y” 设为 “x的右孩子” p[y] ← x                // 将 “y的父节点” 设为 “x”

左旋和右旋对称。

区分左旋和右旋

左旋: 被旋转的节点将变成一个左节点
右旋: 被旋转的节点将变成一个右节点

二叉树插入

TREE-INSERT(T, z)  y ← NIL  x ← T.root  while x ≠ NIL      do y ←  x      if z.key < x.key          then x ← x.left      else x ← x.right  z.p ← y  if y == NIL      then T.root ← z         else if z.key < y.key      then y.left ← z  else y.right ← z  

红黑树的插入

RB-INSERT(T, z)  y ← nil  x ← T.root  while x ≠ T.nil      do y ← x      if z.key < x.key          then x ← x.left      else x ← x.right  z.p ← y  if y == nil[T]      then T.root ← z  else if z.key < y.key      then y.left ← z  else y.right ← z  z.left ← T.nil  z.right ← T.nil  z.color ← RED  RB-INSERT-FIXUP(T, z)  

最后的步骤:把左孩子和右孩子都设置为nil,再把z节点设置为红色。最后调用RB-INSERT-FIXUP函数对其进行修正(重新找色和旋转)

RB-INSERT-FIXUP代码

RB-INSERT-FIXUP(T, z)while color[p[z]] = RED                                                  // 若“当前节点(z)的父节点是红色”,则进行以下处理。    do if p[z] = left[p[p[z]]]                                           // 若“z的父节点”是“z的祖父节点的左孩子”,则进行以下处理。          then y ← right[p[p[z]]]                                        // 将y设置为“z的叔叔节点(z的祖父节点的右孩子)”               if color[y] = RED                                         // Case 1条件:叔叔是红色                  then 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条件:叔叔是黑色,且当前节点是右孩子                          then z ← p[z]                       ▹ Case 2   //  (01) 将“父节点”作为“新的当前节点”。                               LEFT-ROTATE(T, z)              ▹ Case 2   //  (02) 以“新的当前节点”为支点进行左旋。                          color[p[z]] ← BLACK                 ▹ Case 3   // Case 3条件:叔叔是黑色,且当前节点是左孩子。(01) 将“父节点”设为“黑色”。                          color[p[p[z]]] ← RED                ▹ Case 3   //  (02) 将“祖父节点”设为“红色”。                          RIGHT-ROTATE(T, p[p[z]])            ▹ Case 3   //  (03) 以“祖父节点”为支点进行右旋。       else (same as then clause with "right" and "left" exchanged)      // 若“z的父节点”是“z的祖父节点的右孩子”,将上面的操作中“right”和“left”交换位置,然后依次执行。color[root[T]] ← BLACK

根据被插入节点的父节点的情况,分为3中情况来处理:
1. 被插入的节点是根节点:可直接把此节点涂为黑色。
2. 被插入的节点的父节点是黑色:红黑树没有被破坏,无需修正。
3. 被插入的节点的父节点是红色:与特性4冲突(红色节点的两个儿子必须都是黑色)

针对情况3:被插入节点的父亲节点是红色,在这种情况下,其祖父节点一定存在,也存在一个叔叔节点。我们根据叔叔节点的情况,再进一步分为3种情况。

当前节点的父节点是红色
case1 叔叔节点也是红色
case2 叔叔节点是黑色,且当前节点是其父节点的右孩子
case3 叔叔节点是黑色,且当前节点是其父节点的左孩子

原创粉丝点击