5、【数据结构】树形结构之红黑树

一、R-B Tree简介

    R-B Tree,全称是Red-Black Tree,又称为“红黑树”,它一种特殊的二叉查找树红黑树的每个节点上都有存储位表示节点的颜色,可以是红(Red)或黑(Black)。即:红黑树的每个节点上的属性除了有一个key、3个指针:parent、lchild、rchild以外,还多了一个属性:color。它只能是两种颜色:红或黑。红黑树与AVL树都是二叉查找树的平衡版本,相比AVL树的完全平衡,红黑树只要求局部平衡,因此当向红黑树中插入和删除节点时,需要的调整比AVL要少,统计性能要好于AVL树,C++ STL中的map、set、multimap和multiset都应用了红黑树的变体。
红黑树的特性:
  (1)每个节点或者是黑色,或者是红色。
  (2)根节点是黑色。
  (3)每个叶子节点(NIL)是黑色。 [注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!]
  (4)如果一个节点是红色的,则它的子节点必须是黑色的。
  (5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。

注意:
  (1) 特性(3)中的叶子节点,是只为空(NIL或null)的节点。
  (2) 特性(5),确保没有一条路径会比其他路径长出俩倍。因而,红黑树是相对是接近平衡的二叉树。
红黑树的时间复杂度:O(logn)

二、红黑树的基本定义

1、节点定义
typedef int DataType;
enum RBTColor{RED, BLACK};

struct Node
{
    DataType key;//节点键值
    RBTColor color; //节点颜色
    Node *lchild;
    Node *rchild;
    Node *parent;
    //构造函数,使用初始化列表对成员变量进行初始化
    Node(DataType value, RBTColor c, Node *l, Node *r, Node *p)
        :key(value), color(c), lchild(l), rchild(r), parent(p){}
};
2、红黑树定义
class RBTree
{
private:
    Node *root;

public://构造函数、析构函数即外部接口函数的定义
    RBTree();
    ~RBTree();

    //前序遍历
    void preOrder();
    //中序遍历
    void inOrder();
    //后序遍历
    void postOrder();
    //分层遍历
    void levelOrder();
    //在RBTree中查找键值为key的节点(递归实现)
    Node *searchNode(DataType key);
    //查找最小节点:返回最小节点的键值
    DataType minmum();
    //查找最大节点:返回最大节点的键值
    DataType maxmum();
    //查找节点x的前驱结点,即查找RBTree中键值大于x节点的最小值
    Node *successor(Node *x);
    //查找节点x的后继节点,即查找RBTree中键值小于x节点的最大值
    Node *predecessor(Node *x);
    //将节点(键值为key)插入到红黑树中
    void insertNode(DataType key);
    //删除键值为key的节点
    void removeNode(DataType key);
    //销毁红黑树
    void destroyRBTree();
    //打印红黑树
    void printRBTree();
private://内部接口函数的定义
    //前序遍历
    void preOrder(Node *rbroot) const;
    //中序遍历
    void inOrder(Node *rbroot) const;
    //后序遍历
    void postOrder(Node *rbroot) const;
    //分层遍历
    void levelOrder(Node *rbroot) const;
    //在RBTree中查找键值为key的节点(递归实现)
    Node *searchNode(Node *x, DataType key) const;
    //查找最小节点:返回最小节点的键值
    Node *minmum(Node *rbroot);
    //查找最大节点:返回最大节点的键值
    Node *maxmum(Node *rbroot);
    //左旋
    void leftRotation(Node *&root, Node *x);
    //右旋
    void rightRotation(Node *&root, Node *y);
    //将节点(键值为key)插入到红黑树中
    void insertNode(Node *&rbroot, Node *node);
    //插入修正函数
    void insertFixUp(Node *&rbroot, Node *node);
    //删除键值为key的节点
    void removeNode(Node *&rbroot, Node *node);
    //删除修正函数
    void removeFixUp(Node *&rbroot, Node *node, Node *parent);
    //销毁红黑树
    void destroyRBTree(Node *&rbroot);
    //打印红黑树
    void printRBTree(Node *rbroot, DataType key, int direction);

#define rb_parent(r)   ((r)->parent) //获取当前节点r的双亲
#define rb_color(r) ((r)->color)//获取当前节点r的颜色
#define rb_is_red(r)   ((r)->color==RED)//判断当前节点r是否是红色
#define rb_is_black(r)  ((r)->color==BLACK)//判断当前节点r是否是黑色
#define rb_set_black(r)  do { (r)->color = BLACK; } while (0)//设置当前接节点r的颜色为黑色
#define rb_set_red(r)  do { (r)->color = RED; } while (0)//设置当前节点r的颜色为红色
#define rb_set_parent(r,p)  do { (r)->parent = (p); } while (0)//设置当前节点r的双亲为p
#define rb_set_color(r,c)  do { (r)->color = (c); } while (0) //设置当前节点r的颜色为c
};

三、红黑树的基本操作

    红黑树的基本操作是添加、删除。在对红黑树进行添加或删除之后,都会用到旋转方法。为什么呢?道理很简单,添加或删除红黑树中的节点之后,红黑树就发生了变化,可能不满足红黑树的5条性质,也就不再是一颗红黑树了,而是一颗普通的树。而通过旋转,即修改树种某些结点的颜色及指针结构,以达到对红黑树进行插入、删除结点等操作时,红黑树依然能保持它特有的性质(如上文所述的,五点性质)。
    旋转包括两种:左旋 和 右旋。

1、左旋
    对结点x进行左旋,意味着“将结点x变成一个左结点”。《算法导论》中左旋的伪代码如下:
//节点左旋 
void RBTree::leftRotation(Node *&root, Node *x)
  {
      //设置x的右孩子为y
      Node *y = x->rchild;

      //将y的左孩子设为x的右孩子
      //若y的左孩子非空,则将x设为y的左孩子的父节点
      x->rchild = y->lchild;
      if(y->lchild != NULL)
        y->lchild->parent = x;

      //将x的父节点设为y的父节点
      y->parent = x->parent;
      if(x->parent == NULL)
        root = y;   //若x的父节点为空,则将y设为根节点
      else
      {
          if(x->parent->lchild == x)
            x->parent->lchild = y;
          else
            x->parent->rchild = y;
      }

      //将x设为y的左孩子
      y->lchild = x;
      //将x的父节点设为y
      x->parent = y;
  }
2、右旋
    对节点y进行右旋,意味着“将结点y变成一个右结点”。《算法导论》中右旋的伪代码如下:
//节点右旋 
void RBTree::rightRotation(Node *&rbroot, Node *y)
{
    //设x为y节点的左孩子
    Node *x = y->lchild;

    //将x的右孩子,设为y的左孩子
    //若x的右孩子不为空,则将x的右孩子的父节点设为y
    y->lchild = x->rchild;
    if(x->rchild != NULL)
        x->rchild->parent = y;

    //将y的父节点设为x的父节点
    x->parent = y->parent;

    if(y->parent == NULL)
        rbroot = x;
    else
    {
        if(y == y->parent->rchild)
            y->parent->rchild = x;
        else
            y->parent->lchild = x;
    }

    //将y设为x的右孩子
    x->rchild = y;
    //将x设为y的父节点
    y->parent = x;
}
3、如何区分左旋和右旋

    无论是左旋还是右旋,被旋转的树,在旋转前是二叉查找树,并且旋转之后仍然是一颗二叉查找树。
左旋示意图(以x为结点进行左旋):

                               z
   x                          /                  
  / \      --(左旋)-->       x
 y   z                      /
                           y

    对x进行左旋,意味着,将“x的右孩子”设为“x的父亲节点”;即,将 x变成了一个左节点(x成了为z的左孩子)!。 因此,左旋中的“左”,意味着“被旋转的节点将变成一个左节点”。
右旋示意图(以x为结点进行右旋):

                               y
   x                            \                 
  / \      --(右旋)-->           x
 y   z                            \
                                   z

    对x进行右旋,意味着,将“x的左孩子”设为“x的父亲节点”;即,将 x变成了一个右节点(x成了为y的右孩子)! 因此,右旋中的“右”,意味着“被旋转的节点将变成一个右节点”。

4、添加

    将一个节点插入到红黑树中,需要执行哪些步骤呢?首先,将红黑树当作一颗二叉查找树,将节点插入;然后,将节点着色为红色;最后,通过旋转和重新着色等方法来修正该树,使之重新成为一颗红黑树。详细描述如下:
    第一步: 将红黑树当作一颗二叉查找树,将节点插入。
红黑树本身就是一颗二叉查找树,将节点插入后,该树仍然是一颗二叉查找树。也就意味着,树的键值仍然是有序的。此外,无论是左旋还是右旋,若旋转之前这棵树是二叉查找树,旋转之后它一定还是二叉查找树。这也就意味着,任何的旋转和重新着色操作,都不会改变它仍然是一颗二叉查找树的事实。
    **第二步:**将插入的节点着色为"红色"。
为什么着色成红色,而不是黑色呢?为什么呢?在回答之前,我们需要重新温习一下红黑树的特性:
    (1) 每个节点或者是黑色,或者是红色。
    (2) 根节点是黑色。
    (3) 每个叶子节点是黑色。 [注意:这里叶子节点,是指为空的叶子节点!]
    (4) 如果一个节点是红色的,则它的子节点必须是黑色的。
    (5) 从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。
将插入的节点着色为红色,不会违背"特性(5)"!少违背一条特性,就意味着我们需要处理的情况越少。接下来,就要努力的让这棵树满足其它性质即可。
    第三步: 通过一系列的旋转或着色等操作,使之重新成为一颗红黑树。
    第二步中,将插入节点着色为"红色"之后,不会违背"特性(5)"。那它到底会违背哪些特性呢?
    对于"特性(1)",显然不会违背了。因为我们已经将它涂成红色了。
    对于"特性(2)",显然也不会违背。在第一步中,我们是将红黑树当作二叉查找树,然后执行的插入操作。而根据二叉查找数的特点,插入操作不会改变根节点。所以,根节点仍然是黑色。
    对于"特性(3)",显然不会违背了。这里的叶子节点是指的空叶子节点,插入非空节点并不会对它们造成影响。
    对于"特性(4)",是有可能违背的!那接下来,想办法使之"满足特性(4)",就可以将树重新构造成红黑树了。

插入实现代码如下:

//插入节点
//将节点node插入到的红黑树中
void RBTree::insertNode(Node *&rbroot, Node *node)
{
    Node *y = NULL;
    Node *x = rbroot;

    //1.将红黑树当做一颗二叉查找树,将节点插入二叉查找树中
    while(x != NULL)
    {
        y = x;
        if(node->key < x->key)
            x = x->lchild;
        else
            x = x->rchild;
    }
    node->parent = y;

    if(y != NULL)
    {
        if(node->key < y->key)
            y->lchild = node;
        else
            y->rchild = node;
    }
    else
    {
        rbroot = node;
    }

    //2.设置节点颜色
    node->color = RED;

    //3.将它重新修正为一颗红黑树
    insertFixUp(root, node);
}
//将键值为key的节点插入到红黑树
void RBTree::insertNode(DataType key)
{
    Node *z = NULL;
    //若新建节点失败,则返回
    if((z = new Node(key, BLACK, NULL, NULL, NULL)) == NULL)
        return;
    insertNode(root, z);
}

染色和旋转的代码实现如下:

//红黑树调整 
void RBTree::insertFixUp(Node *&rbroot, Node *node)
{
    Node *parent, *gparent;

    //若父节点存在,并且父节点的颜色为红色
    while((parent = rb_parent(node)) && rb_is_red(parent))
    {
        gparent = rb_parent(parent);

        //若父节点是祖父节点的左孩子
        if(parent == gparent->lchild)
        {
            {
                //case1:叔叔节点是红色的
                Node *uncle = gparent->rchild;
                if(uncle && rb_is_red(uncle))
                {
                    rb_set_black(uncle);
                    rb_set_black(parent);
                    rb_set_red(gparent);
                    node = gparent;
                    continue;
                }
            }
            //case2:叔叔是黑色,且当前节点是右孩子
            if(parent->rchild == node)
            {
                Node *temp;
                leftRotation(root, parent);
                temp = parent;
                parent = node;
                node = temp;
            }
            //case3:叔叔是黑色的,且当前节点是左孩子
            rb_set_black(parent);
            rb_set_red(gparent);
            rightRotation(root, gparent);
        }
        else//若父节点是祖父节点的右孩子
        {
            {
                //case1:叔叔是红色的
                Node *uncle = gparent->lchild;
                if(uncle && rb_is_red(uncle))
                {
                    rb_set_black(uncle);
                    rb_set_black(parent);
                    rb_set_red(gparent);
                    node = gparent;
                    continue;
                }
            }
            //case2:叔叔是黑色,且当前节点是左孩子
            if(parent->lchild == node)
            {
                Node *temp;
                rightRotation(root, parent);
                temp = parent;
                parent = node;
                node = temp;
            }

            //case3:叔叔是黑色的,且当前节点是右孩子
            rb_set_black(parent);
            rb_set_red(gparent);
            leftRotation(root, gparent);
        }
    }
    //将根节点设为黑色
    rb_set_black(root);
}

    根据被插入节点的父节点的情况,可以将"当节点z被着色为红色节点,并插入二叉树"划分为三种情况来处理。
    ① 情况说明:被插入的节点是根节点。
    处理方法:直接把此节点涂为黑色。
    ② 情况说明:被插入的节点的父节点是黑色。
    处理方法:什么也不需要做。节点被插入后,仍然是红黑树。
    ③ 情况说明:被插入的节点的父节点是红色。
    处理方法:那么,该情况与红黑树的“特性(5)”相冲突。这种情况下,被插入节点是一定存在非空祖父节点的;进一步的讲,被插入节点也一定存在叔叔节点(即使叔叔节点为空,我们也视之为存在,空节点本身就是黑色节点)。理解这点之后,我们依据"叔叔节点的情况",将这种情况进一步划分为3种情况(Case)。

现象说明 处理策略
case 1     当前节点的父节点是红色,且当前节点的祖父节点的另一个子节点(叔叔节点)也是红色。 (01) 将“父节点”设为黑色。
(02) 将“叔叔节点”设为黑色。
(03) 将“祖父节点”设为“红色”。
(04) 将“祖父节点”设为“当前节点”(红色节点);即,之后继续对“当前节点”进行操作。
case 2     当前节点的父节点是红色,叔叔节点是黑色,且当前节点是其父节点的右孩子 (01) 将“父节点”作为“新的当前节点”。
(02) 以“新的当前节点”为支点进行左旋。
case 3     当前节点的父节点是红色,叔叔节点是黑色,且当前节点是其父节点的左孩子 (01) 将“父节点”设为“黑色”。
(02) 将“祖父节点”设为“红色”。
(03) 以“祖父节点”为支点进行右旋。
##### 5、删除

    将红黑树内的某一个节点删除。需要执行的操作依次是:首先,将红黑树当作一颗二叉查找树,将该节点从二叉查找树中删除;然后,通过"旋转和重新着色"等一系列来修正该树,使之重新成为一棵红黑树。详细描述如下:
    第一步:将红黑树当作一颗二叉查找树,将节点删除。
这和"删除常规二叉查找树中删除节点的方法是一样的"。分3种情况:
    ① 被删除节点没有儿子,即为叶节点。那么,直接将该节点删除就OK了。
    ② 被删除节点只有一个儿子。那么,直接删除该节点,并用该节点的唯一子节点顶替它的位置。
    ③ 被删除节点有两个儿子。那么,先找出它的后继节点;然后把“它的后继节点的内容”复制给“该节点的内容”;之后,删除“它的后继节点”。
    在这里,后继节点相当于替身,在将后继节点的内容复制给"被删除节点"之后,再将后继节点删除。这样就巧妙的将问题转换为"删除后继节点"的情况了,下面就考虑后继节点。 在"被删除节点"有两个非空子节点的情况下,它的后继节点不可能是双子非空。既然"的后继节点"不可能双子都非空,就意味着"该节点的后继节点"要么没有儿子,要么只有一个儿子。若没有儿子,则按"情况① "进行处理;若只有一个儿子,则按"情况② "进行处理。
    第二步:通过"旋转和重新着色"等一系列来修正该树,使之重新成为一棵红黑树。
    因为"第一步"中删除节点之后,可能会违背红黑树的特性。所以需要通过"旋转和重新着色"来修正该树,使之重新成为一棵红黑树。
插入实现代码如下:

//从红黑树删除节点node
void RBTree::removeNode(Node *&root, Node *node)
{
    Node *child, *parent;
    RBTColor color;

    //被删除节点的左右孩子都不为空的情况
    if((node->lchild != NULL) && (node->rchild != NULL))
    {
        //被删除节点的后继节点(称为取代节点)
        //用它来取代“被删除节点”的位置,然后再将“被删除节点”去掉
        Node *replace = node;

        //获取后继节点
        replace = replace->rchild;
        while(replace->lchild != NULL)
            replace = replace->lchild;

        //node节点不是根节点(只有根节点不存在父节点)
        if(rb_parent(node))
        {
            if(rb_parent(node)->lchild == node)
                rb_parent(node)->lchild = replace;
            else
                rb_parent(node)->rchild = replace;
        }
        else//node节点是根节点,则更新根节点
            root = replace;

        //child是取代节点的右孩子,也是需要调整的节点
        //取代节点肯定不存在左孩子,因为,他是一个后继节点
        child = parent->rchild;
        parent = rb_parent(replace);
        //保存取代节点的颜色
        color = rb_color(replace);

        //被删除节点是它的后继节点的父节点
        if(parent == node)
        {
            parent = replace;
        }
        else
        {
            //child不为空
            if(child)
                rb_set_parent(child, parent);
            parent->lchild = child;

            replace->rchild = node->rchild;
            rb_set_parent(node->rchild, replace);
        }

        replace->parent = node->parent;
        replace->color = node->color;
        replace->lchild = node->lchild;
        node->lchild->parent = replace;

        if(color == BLACK)
            removeFixUp(root, child, parent);
        delete node;
        return;
    }

    if(node->lchild != NULL)
        child = node->lchild;
    else
        child = node->rchild;

    parent = node->parent;
    //保存取代节点的颜色
    color = node->color;

    if(child)
        child->parent = parent;

    //node节点不是根节点
    if(parent)
    {
        if(parent->lchild == node)
            parent->lchild = child;
        else
            parent->rchild = child;
    }
    else
        root = child;

    if(color == BLACK)
        removeFixUp(root, child, parent);
    delete node;
}
void RBTree::removeNode(DataType key)
{
    Node *node;
    //查找key对应的节点(node),找到的话就删除该节点
    if((node = searchNode(root, key)) != NULL)
        removeNode(root, node);
}

染色和旋转的代码实现如下:

//红黑树节点删除修正函数
void RBTree::removeFixUp(Node *&root, Node *node, Node *parent)
{
    Node *other;

    while(!node || rb_is_black(node) && node != root)
    {
        if(parent->lchild == node)
        {
            other = parent->rchild;
            if(rb_is_red(other))
            {
                //case1:x的兄弟节点w是红色的
                rb_set_black(other);
                rb_set_red(parent);
                leftRotation(root, parent);
                other = parent->rchild;
            }
            if((!other->lchild || rb_is_black(other->lchild))
               && (!other->rchild || rb_is_black(other->rchild)))
            {
                //case2:x的兄弟节点w是黑色,且w的两个孩子也是黑色的
                rb_set_red(other);
                node = parent;
                parent = rb_parent(node);
            }
            else
            {
                if(!other->rchild || rb_is_black(other->rchild))
                {
                    //case3:x的兄弟w是黑色的,并且w的左孩子是红色,右孩子是黑色
                    rb_set_black(other->lchild);
                    rb_set_red(other);
                    rightRotation(root, other);
                    other = parent->rchild;
                }
                //case4:x的兄弟节点w是黑色的,并且w的右孩子是红色的,左孩子是任意颜色
                rb_set_color(other, rb_color(parent));
                rb_set_black(parent);
                rb_set_black(other->rchild);
                leftRotation(root, parent);
                node = root;
                break;
            }
        }
        else
        {
            other = parent->lchild;
            if(rb_is_red(other))
            {
                //case1:x的兄弟w是红色的
                rb_set_black(other);
                rb_set_red(parent);
                rightRotation(root, parent);
                other = parent->lchild;
            }
            if((!other->lchild || rb_is_black(other->lchild)) &&
               (!other->rchild || rb_is_black(other->rchild)))
            {
                //case2:x的兄弟w是黑色,且w的两个孩子也都是黑色的
                rb_set_red(other);
                node = parent;
                parent = rb_parent(node);
            }
            else
            {
                if(!other->lchild || rb_is_black(other->lchild))
                {
                    //case3:x的兄弟w是黑色的,且w的左孩子是红色的,右孩子是黑色的
                    rb_set_black(other->rchild);
                    rb_set_red(other);
                    leftRotation(root, other);
                    other = parent->lchild;
                }
                //case4:x的兄弟w是黑色的,且w的右孩子是红色,左孩子是任意颜色
                rb_set_color(other, rb_color(parent));
                rb_set_black(parent);
                rb_set_black(other->lchild);
                rightRotation(root, parent);
                node = root;
                break;
            }
        }
    }
    if(node)
        rb_set_black(node);
}

    下面对删除函数进行分析。在分析之前,我们再次温习一下红黑树的几个特性:
    (1) 每个节点或者是黑色,或者是红色。
    (2) 根节点是黑色。
    (3) 每个叶子节点是黑色。 [注意:这里叶子节点,是指为空的叶子节点!]
    (4) 如果一个节点是红色的,则它的子节点必须是黑色的。
    (5) 从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。
    前面我们将"删除红黑树中的节点"大致分为两步,在第一步中"将红黑树当作一颗二叉查找树,将节点删除"后,可能违反"特性(2)、(4)、(5)“三个特性。第二步需要解决上面的三个问题,进而保持红黑树的全部特性。
    为了便于分析,我们假设"x包含一个额外的黑色”(x原本的颜色还存在),这样就不会违反"特性(5)"。为什么呢?
    通过removeNode算法,我们知道:删除节点y之后,x占据了原来节点y的位置。 既然删除y(y是黑色),意味着减少一个黑色节点;那么,再在该位置上增加一个黑色即可。这样,当我们假设"x包含一个额外的黑色",就正好弥补了"删除y所丢失的黑色节点",也就不会违反"特性(5)"。 因此,假设"x包含一个额外的黑色"(x原本的颜色还存在),这样就不会违反"特性(5)"。
    现在,x不仅包含它原本的颜色属性,x还包含一个额外的黑色。即x的颜色属性是"红+黑"或"黑+黑",它违反了"特性(1)"。
    现在,我们面临的问题,由解决"违反了特性(2)、(4)、(5)三个特性"转换成了"解决违反特性(1)、(2)、(4)三个特性"。removeFixUp需要做的就是通过算法恢复红黑树的特性(1)、(2)、(4)。
    removeFixUp的思想是:将x所包含的额外的黑色不断沿树上移(向根方向移动),直到出现下面的姿态:
    a) x指向一个"红+黑"节点。此时,将x设为一个"黑"节点即可。
    b) x指向根。此时,将x设为一个"黑"节点即可。
    c) 非前面两种姿态。
将上面的姿态,可以概括为3种情况。
    ① 情况说明:x是“红+黑”节点。
    处理方法:直接把x设为黑色,结束。此时红黑树性质全部恢复。
    ② 情况说明:x是“黑+黑”节点,且x是根。
    处理方法:什么都不做,结束。此时红黑树性质全部恢复。
    ③ 情况说明:x是“黑+黑”节点,且x不是根。
    处理方法:这种情况又可以划分为4种子情况。这4种子情况如下表所示:

现象说明 处理策略
case 1     x是"黑+黑"节点,x的兄弟节点是红色。(此时x的父节点和x的兄弟节点的子节点都是黑节点)。 (01) 将x的兄弟节点设为“黑色”。
(02) 将x的父节点设为“红色”。
(03) 对x的父节点进行左旋。
(04) 左旋后,重新设置x的兄弟节点。
case 2     x是“黑+黑”节点,x的兄弟节点是黑色,x的兄弟节点的两个孩子都是黑色。 (01) 将x的兄弟节点设为“红色”。
(02) 设置“x的父节点”为“新的x节点”。
case 3     x是“黑+黑”节点,x的兄弟节点是黑色;x的兄弟节点的左孩子是红色,右孩子是黑色的。 (01) 将x兄弟节点的左孩子设为“黑色”。
(02) 将x兄弟节点设为“红色”。
(03) 对x的兄弟节点进行右旋。
(04) 右旋后,重新设置x的兄弟节点。
case 4     x是“黑+黑”节点,x的兄弟节点是黑色;x的兄弟节点的右孩子是红色的,x的兄弟节点的左孩子任意颜色。 (01) 将x父节点颜色 赋值给 x的兄弟节点。
(02) 将x父节点设为“黑色”。
(03) 将x兄弟节点的右子节设为“黑色”。
(04) 对x的父节点进行左旋。
(05) 设置“x”为“根节点”。
[完整的示例代码](https://github.com/KHdvip/Data-Structures-and-Algorithm/tree/master/RBTree) 参考文章:http://www.cnblogs.com/skywang12345/p/3624291.html

猜你喜欢

转载自blog.csdn.net/sinat_33924041/article/details/83418381