红黑树笔记——红黑树的插入操作

来源:互联网 发布:c语言求最小公倍数 编辑:程序博客网 时间:2024/04/29 01:14

红黑树的插入操作可以在O(logn)的时间内完成。开始插入节点的时候和二叉查找树一样,只需要最后将插入的节点着成红色,为了保证红黑树的性质,需要通过RB_InsertFixUp函数来调整该节点,对其重新着色并旋转。

下面先调用RB_Insert()函数将一个节点插入到红黑树中,同样先上伪代码

RB-INSERT(T, z) 1  y ← nil[T] 2  x ← root[T] 3  while x ≠ nil[T] 4      do y ← x 5         if key[z] < key[x] 6            then x ← left[x] 7            else x ← right[x] 8  p[z] ← y 9  if y = nil[T]10     then root[T] ← z11     else if key[z] < key[y]12             then left[y] ← z13             else right[y] ← z14  left[z] ← nil[T]15  right[z] ← nil[T]16  color[z] ← RED17  RB-INSERT-FIXUP(T, z)
接着上C++代码并进行代码分析

bool RB_Insert(int key, int data)    {        RB_Node *p = root;        RB_Node *insert_p = NIL;        //找到插入在哪个节点之后        while(p != NIL)        {            insert_p = p;            if(key < p->key) p = p->left;            else if(key > p->key) p = p->right;            else return false;//重复的数据不进行插入        }        //初始化插入节点,注意插入节点初始的颜色为RED        RB_Node *insert_node = new RB_Node();        insert_node->key = key;        insert_node->data = data;        insert_node->RB_COLOR = RED;        insert_node->right = NIL;        insert_node->left = NIL;        //如果插入的是一颗空树,设定root就是插入节点        if(insert_p == NIL)        {            root = insert_node;            root->parent = NIL;            NIL->left = NIL->right = NIL->parent = root;        }        else//判断插入左节点还是右节点        {            if(key < insert_p->key)                insert_p->left = insert_node;            else insert_p->right = insert_node;            insert_node->parent = insert_p;        }        //修复红黑树的性质        RB_InsertFixUp(insert_node);        return true;    }
为了保证红黑树的结构性质,需要调用RB_InsertFixUp函数来调整刚刚插入的节点

我们考虑下,如果一个红色节点(下文称用Z指向它)被插入到树中,那么有哪些红黑性质可能被破坏呢?只有性质2(根节点是黑色的,由于插入的可能是根节点)以及性质4(红色节点的子节点一定是黑色节点,其父节点是红色的),其它都不会被破坏。

如果插入的节点的父节点是黑色的,那么不需要做任何调整,这红黑树是正常的。如果父节点是红色,或插在树根的位置,那么就要进行调整以保证红黑树性质。

首先对性质4进行分析,我们知道,插入一个新的节点,这个节点肯定会被放到树的底部成为一个叶节点,那么这个红色节点就没有可能和自己的子节点同色(因为叶节点的子节点是NIL节点,都是黑色的),如果性质4被破坏的话,肯定是Z指向的节点的父节点是红色的。因此,为了使分析和解决更加容易和清晰,我们在对树进行调整以恢复红黑特性时,始终使得Z总是指向相邻红色的节点中的子节点(指针Z可能会向上升到树的中部或根部)。基于这个做法,我们可以知道,如果是性质2被破坏了的话,也就是Z指向根节点了,那么性质4肯定就符合了(因为Z的父节点是NIL,黑色的),因此对性质2的恢复变得很简单,只需要被根从红色变为黑色即可。
现在只需要处理性质4被破坏的问题,如果性质4被破坏了,也就是说,Z的父节点是红色的,那么,说明,Z一定有祖父节点,而且是黑色的(否则插入前原树就有问题,又或是调整时的方法不正确)。因此可以把问题放到以Z的祖父节点为根节点的子树内进行解决,这样可以把调整的范围最小化,而且这也是有可能的:只要不改变这子树的黑高度,那么就不会对树的其它部分产生影响。我们要做的就是在这个子树范围内把红黑性质调整回来。再看子树的根是否与其父节点同为红色,是的话,就再次用前面所说的去解决它,一直向上递归到红黑性质被恢复为止。

对性质4的恢复,根据Z的父节点是Z的祖节点的左子节点还是右子节点,分为两组对称的情况,每组有3种情况。下面我们以Z的父节点是Z祖节点的左子节点为例:  

定义叔叔的含义:即祖父节点的另外一个子节点

情况1:z的叔叔y是红色的

RBTree1

如上图所示,在这种情况下,将父、叔节点都着为黑色,再将子树根节点着为红色,那么子树的黑高度没有发生改变,而且红黑性质得得到了调整。此时,再将Z指向子树的根节点,向上递归恢复红黑特性。

情况2:z的叔叔y节点是黑色的,而且z是父节点的左子节点


如上图,将Z的父节点与祖节点进行一次右旋,并把父节点着黑色,原来的祖节点着红色。这些子树的红黑特性得到了恢复,而且子树的黑高度没有变化。另外,由于子树根节点已经是黑色了(这个节点不会出现父子同为红色的问题了),所以不必再向上递归了,此时整个树的红黑特性都已经是正确的了。

情况3:z的叔叔y节点是黑色的,而且z是父节点的右子节点


如上图,将Z本身与其父节点进行一次左旋,让Z指向原来的父节点,就可以调整为情况2,而情况2已经得到解决。

由此红黑树插入后调整问题已经解决。

伪代码:

 1 while color[p[z]] = RED 2     do if p[z] = left[p[p[z]]] 3           then y ← right[p[p[z]]] 4                if color[y] = RED 5                   then color[p[z]] ← BLACK                    // Case 1 6                        color[y] ← BLACK                       // Case 1 7                        color[p[p[z]]] ← RED                   // Case 1 8                        z ← p[p[z]]                            // Case 1 9                   else if z = right[p[z]]10                           then z ← p[z]                       // Case 211                                LEFT-ROTATE(T, z)               // Case 212                           color[p[z]] ← BLACK                 // Case 313                           color[p[p[z]]] ← RED                // Case 314                           RIGHT-ROTATE(T, p[p[z]])             // Case 315           else (same as then clause                         with "right" and "left" exchanged)16 color[root[T]] ← BLACK

RB_InsertFixUp C++源码:

 void RB_InsertFixUp(RB_Node *node)    {        while(node->parent->RB_COLOR == RED)        {            if(node->parent == node->parent->parent->left)            {                RB_Node *uncle = node->parent->parent->right;                //情况1,z的叔叔y是红色的                if(uncle->RB_COLOR == RED)                {                    node->parent->RB_COLOR = BLACK;                    uncle->RB_COLOR = BLACK;                    node->parent->parent->RB_COLOR = RED;                    node = node->parent->parent;                }                                else if(uncle->RB_COLOR == BLACK)                {                    //情况3,z的叔叔y是黑色的,z是右孩子                    if(node == node->parent->right)                    {                        node = node->parent;                        RB_LeftRotate(node);                    }                    //情况2,z的叔叔y是黑色的,但z是左孩子                    else                    {                        node->parent->RB_COLOR = BLACK;                        node->parent->parent->RB_COLOR = RED;                        RB_RightRotate(node->parent->parent);                    }                }            }            else            {                RB_Node *uncle = node->parent->parent->left;                if(uncle->RB_COLOR == RED)                {                    node->parent->RB_COLOR = BLACK;                    uncle->RB_COLOR = BLACK;                    uncle->parent->RB_COLOR = RED;                    node = node->parent->parent;                }                else if (uncle->RB_COLOR == BLACK)                {                    if(node == node->parent->left)                    {                        node = node->parent;                        RB_RightRotate(node);                    }                    else                    {                        node->parent->RB_COLOR = BLACK;                        node->parent->parent->RB_COLOR = RED;                        RB_LeftRotate(node->parent->parent);                    }                }            }        }        root->RB_COLOR = BLACK;    }















原创粉丝点击