录十六

持之以恒

红黑树算法

一、红黑树简介

红黑树(R-B Tree),全称Red-Black Tree。它是一种自平衡二叉查找树,是计算机科学中一种非常重要的数据结构。红黑树实现比较复杂,但是它具有高效,稳定的查找效率,所以在很多地方都有应用。在C++ STL中,set, multiset, map, multimap底层都是基于红黑树实现的。

(1)红黑树性质

红黑树是一种普通的二叉树查找上,仅仅只是对每个节点添加一个颜色属性形成的,它具有以下五条性质:

  • 节点是红色或黑色。

  • 根节点是黑色。

  • 每个叶子节点(NIL节点,空节点)都是黑色的。

  • 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)

  • 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。

mmexport1526111222165.jpg

二、红黑树的基本操作

(1)左旋操作

左旋操作是用来修正红结点出现在右边的情况。操作主要步骤:

  • x的右孩子变成y的左孩子

  • y的左孩子变成x

  • y作为新的子链返回

void _Rb_tree_rotate_left(_Rb_tree_node_base* node_x, _Rb_tree_node_base*& __root)
{
    //获取y节点
    _Rb_tree_node_base* node_y = node_x->_M_right;

    //1. x节点的右孩子变成y的左孩子,同时修正y节点左孩子的父节点指针
    node_x->_M_right = node_y->_M_left;
    if (node_y->_M_left != 0)
    {
        node_y->_M_left->_M_parent = node_x;
    }

    //2. 修正y的父节点指针,然后将y作为子链返回
    node_y->_M_parent = node_x->_M_parent;
    if (node_x == __root)
    {
        //如果x节点根节点,则使用新的y子链更新根节点
        __root = node_y;
    }
    else if (node_x == node_x->_M_parent->_M_left)
    {
        //如果x节点是左节点,则使用新的y子链更新
        node_x->_M_parent->_M_left = node_y;
    }
    else
    {
        //如果x节点是右节点,则使用新的y子链更新
        node_x->_M_parent->_M_right = node_y;
    }
    
    //3. 将y的左孩子变成x,同时修正x的父节点指针
    node_y->_M_left = node_x;
    node_x->_M_parent = node_y;
}

(2)右旋操作

  • x的左孩子变成y的右孩子

  • y的右孩子变成x

  • y作为新的子链返回

void _Rb_tree_rotate_right(_Rb_tree_node_base* node_x, _Rb_tree_node_base*& __root)
{
    //获取y节点
    _Rb_tree_node_base* node_y = node_x->_M_left;

    //1. x节点的左孩子变成y的右孩子,同时修正y节点右孩子的父节点指针
    node_x->_M_left = node_y->_M_right;
    if (node_y->_M_right != 0)
    {
        node_y->_M_right->_M_parent = node_x;
    }
    
    //2. 修正y的父节点指针,然后将y作为子链返回
    node_y->_M_parent = node_x->_M_parent;
    if (node_x == __root)
    {
        //如果x节点根节点,则使用新的y子链更新根节点
        __root = node_y;
    }
    else if (node_x == node_x->_M_parent->_M_right)
    {
        //如果x节点是右节点,则使用新的y子链更新
        node_x->_M_parent->_M_right = node_y;
    }
    else
    {
        //如果x节点是左节点,则使用新的y子链更新
        node_x->_M_parent->_M_left = node_y;
    }

    //3. 将y的右孩子变成x,同时修正x的父节点指针
    node_y->_M_right = node_x;
    node_x->_M_parent = node_y;
}

三、红黑树的插入操作

(1)将红黑树当作一颗二叉查找树,将节点插入

(2)将插入的节点着色为红色

(3)通过一系列旋转或着色等操作,使之重新成为一颗红黑树



一级分类二级分类三级分类处理策略
case 1x是根节点


(1) 根节点设为黑色
case 2x的父节点是黑色


case 3x的父节点是红色
x的父节点是左孩子,叔节点是红色
(1) x的父节点和叔节点设为黑色
(2) x祖父节点设为红色
(3) x的祖父节点作为新节点(即x节点),循环对x节点执行上述操作
case 4x的父节点是右孩子,叔节点是红色
case 5x的父节点是左孩子,叔节点是黑色x是左孩子(1) x的父节点设为黑色、叔父节点设为红色
(2) 以x的祖父节点进行右旋
case 6x是右孩子(1) x的父节点作为新的节点(即x节点)
(2) 以x节点进行左旋
(3) 再按照x是左孩子情况继续处理
case 7x的父节点是右孩子,叔节点是黑色x是左孩子(1) x的父节点作为新的节点(即x节点)
(2) 以x节点进行右旋
(3) 再按照x是右孩子情况继续处理
case 8x是右孩子(1)x的父节点设为黑色、叔父节点设为红色
(2)以x的祖父节点进行左旋


//用于插入新节点后重新形成红黑树,node_x为指向新增节点的指针,__root为指向根节点的指针
 void _Rb_tree_rebalance(_Rb_tree_node_base* node_x, _Rb_tree_node_base*& __root)
{
    //将新插入的节点涂成红色
    node_x->_M_color = _S_rb_tree_red;

    //1. (如果x是根节点),或(x的父节点是黑色),则不进入循环,将根节点涂黑即可。
    //2. (如果x是非根节点,并且父节点是红色),则进入循环,调整树使之满足红黑树性质。
    while (node_x != __root && node_x->_M_parent->_M_color == _S_rb_tree_red)
    {
        //x节点的父节点是左节点
        if (node_x->_M_parent == node_x->_M_parent->_M_parent->_M_left)
        {
            // 取出x节点的叔节点
            _Rb_tree_node_base* node_y = node_x->_M_parent->_M_parent->_M_right;
            
            //如果叔节点是红色
            if (node_y && node_y->_M_color == _S_rb_tree_red) 
            {
                node_x->_M_parent->_M_color = _S_rb_tree_black;
                node_y->_M_color = _S_rb_tree_black;
                node_x->_M_parent->_M_parent->_M_color = _S_rb_tree_red;

                //将祖父节点设为当前节点;继续对当前节点进行操作
                node_x = node_x->_M_parent->_M_parent;
            }
            else 
            {
                //叔节点是空或黑色(按规定,空节点也是黑色),且x节点是父节点的右孩子
                if (node_x == node_x->_M_parent->_M_right)
                {
                    //将x的父节点作为新节点,并以新节点为支点进行左旋
                    node_x = node_x->_M_parent;
                    _Rb_tree_rotate_left(node_x, __root);
                }
                //将父节点设为黑色
                node_x->_M_parent->_M_color = _S_rb_tree_black;
                //将祖父节点设为红色
                node_x->_M_parent->_M_parent->_M_color = _S_rb_tree_red;
                // 以祖父节点为支点进行右旋
                _Rb_tree_rotate_right(node_x->_M_parent->_M_parent, __root);
            }
        }
        else  //x节点的父节点是右节点
        {
             // 取出x节点的叔节点
            _Rb_tree_node_base* node_y = node_x->_M_parent->_M_parent->_M_left;
            
            //如果叔节点是红色
            if (node_y && node_y->_M_color == _S_rb_tree_red)
            {
                node_x->_M_parent->_M_color = _S_rb_tree_black;
                node_y->_M_color = _S_rb_tree_black;
                node_x->_M_parent->_M_parent->_M_color = _S_rb_tree_red;
    
                //将祖父节点设为当前节点;继续对当前节点进行操作
                node_x = node_x->_M_parent->_M_parent;
            }
            else
            {
                //叔节点是空或黑色(按规定,空节点也是黑色),且x节点是父节点的左孩子
                if (node_x == node_x->_M_parent->_M_left) 
                {
                    //将x的父节点作为新节点,并以新节点为支点进行右旋
                    node_x = node_x->_M_parent;
                    _Rb_tree_rotate_right(node_x, __root);
                }
                //将父节点设为黑色
                node_x->_M_parent->_M_color = _S_rb_tree_black;
                //将祖父节点设为红色
                node_x->_M_parent->_M_parent->_M_color = _S_rb_tree_red;
                //以祖父节点为支点进行左旋
                _Rb_tree_rotate_left(node_x->_M_parent->_M_parent, __root);
            }
        }
    }
    __root->_M_color = _S_rb_tree_black;
}

四、红黑树的删除操作

(1)将红黑树当作一颗二叉查找树,将节点删除。

(2)通过"旋转和重新着色"等一系列来修正该树,使之重新成为一棵红黑树。

//用于删除节点后调整树形, __z为待删除点,__root为根节点,__leftmost左极点,__rightmost为右极点
_Rb_tree_node_base* _Rb_tree_rebalance_for_erase(_Rb_tree_node_base* __z, 
                                                 _Rb_tree_node_base*& __root, 
                                                 _Rb_tree_node_base*& __leftmost, 
                                                 _Rb_tree_node_base*& __rightmost)
{
    //暂存要删除的节点
    _Rb_tree_node_base* node_y = __z;

    _Rb_tree_node_base* node_x = 0;
    _Rb_tree_node_base* node_x_parent = 0;
    if (node_y->_M_left == 0)     
    {
        node_x = node_y->_M_right;     // node_x might be null.
    }
    else
    {
        if (node_y->_M_right == 0)  
        {
            node_x = node_y->_M_left;    // node_x is not null.
        }
        else
        {   
            // __z的左右子树非空
            //寻找y(即z)的右子树中,值最小的节点
            node_y = node_y->_M_right;   //   __z's successor.  node_x might be null.
            while (node_y->_M_left != 0)
            {
                node_y = node_y->_M_left;
            }
            //取最小的节点的右节点
            node_x = node_y->_M_right;
        }
    }

    if (node_y != __z)
    {
        //case: 针对__z左右子非空的删除操作。

        __z->_M_left->_M_parent = node_y;
        node_y->_M_left = __z->_M_left;

        if (node_y != __z->_M_right)
        {
            node_x_parent = node_y->_M_parent;//记录(替换__z的节点)的父节点
            if (node_x)
            {
                node_x->_M_parent = node_y->_M_parent;
            }
            node_y->_M_parent->_M_left = node_x;  // __y must be a child of _M_left
            node_y->_M_right = __z->_M_right;
            __z->_M_right->_M_parent = node_y;
        }
        else
        {
            node_x_parent = node_y; //记录(替换__z的节点)的父节点
        }

        //删除__z节点
        if (__root == __z)
        {
            __root = node_y;
        }
        else if (__z->_M_parent->_M_left == __z)
        {
            __z->_M_parent->_M_left = node_y;
        }
        else
        {
            __z->_M_parent->_M_right = node_y;
        }

        node_y->_M_parent = __z->_M_parent;
        //维持颜色不变
        std_swap(node_y->_M_color, __z->_M_color);
        node_y = __z; //指向实际需要删除的节点
    }
    else
    {
        //case: 针对__z至少存在一个空子节点的删除操作。
        //取出y(即z节点)的父节点,
        node_x_parent = node_y->_M_parent;

        //更新x的父节点
        if (node_x)
        {
            node_x->_M_parent = node_y->_M_parent;
        }

        if (__root == __z)
        {
            __root = node_x;
        }
        else
        {
            //如果z是左节点,直接删除z节点
            if (__z->_M_parent->_M_left == __z)
            {    
                __z->_M_parent->_M_left = node_x;
            }
            else //如果z是右节点,直接删除z节点
            {
                __z->_M_parent->_M_right = node_x;
            }
        }

        //更新左极值点,stl附加功能,非红黑树必须操作
        if (__leftmost == __z)
        {
            if (__z->_M_right == 0)        // __z->_M_left must be null also
            {
                __leftmost = __z->_M_parent;
                // makes __leftmost == _M_header if __z == __root
            }
            else
            {
                __leftmost = _Rb_tree_node_base::_S_minimum(node_x);
            }
        }

        //更新右极值点,stl附加功能,非红黑树必须操作
        if (__rightmost == __z)
        {
            if (__z->_M_left == 0)         // __z->_M_right must be null also
            {
                __rightmost = __z->_M_parent;
                // makes __rightmost == _M_header if __z == __root
            }
            else                      // node_x == __z->_M_left
            {
                __rightmost = _Rb_tree_node_base::_S_maximum(node_x);
            }
        }
    }

    //删除的是红节点,无需处理。所以这里只针对node_y是非红节点进行处理。
    if (node_y->_M_color != _S_rb_tree_red)
    {
        while (node_x != __root && (node_x == 0 || node_x->_M_color == _S_rb_tree_black))
        {
            if (node_x == node_x_parent->_M_left)
            {
                //case: node_x是左节点
                _Rb_tree_node_base* node_w = node_x_parent->_M_right;
                if (node_w->_M_color == _S_rb_tree_red)
                {
                    node_w->_M_color = _S_rb_tree_black;
                    node_x_parent->_M_color = _S_rb_tree_red;
                    _Rb_tree_rotate_left(node_x_parent, __root);
                    node_w = node_x_parent->_M_right;
                }

                if ((node_w->_M_left == 0 || node_w->_M_left->_M_color == _S_rb_tree_black) &&
                    (node_w->_M_right == 0 || node_w->_M_right->_M_color == _S_rb_tree_black))
                {
                    node_w->_M_color = _S_rb_tree_red;
                    node_x = node_x_parent;
                    node_x_parent = node_x_parent->_M_parent;
                }
                else
                {
                    if (node_w->_M_right == 0 || node_w->_M_right->_M_color == _S_rb_tree_black)
                    {
                        if (node_w->_M_left)
                        {
                            node_w->_M_left->_M_color = _S_rb_tree_black;
                        }

                        node_w->_M_color = _S_rb_tree_red;
                        _Rb_tree_rotate_right(node_w, __root);
                        node_w = node_x_parent->_M_right;
                    }
                    node_w->_M_color = node_x_parent->_M_color;
                    node_x_parent->_M_color = _S_rb_tree_black;

                    if (node_w->_M_right)
                    {
                        node_w->_M_right->_M_color = _S_rb_tree_black;
                    }

                    _Rb_tree_rotate_left(node_x_parent, __root);
                    break;
                }
            }
            else
            { 
                //case: node_x是右节点
                _Rb_tree_node_base* node_w = node_x_parent->_M_left;
                if (node_w->_M_color == _S_rb_tree_red)
                {
                    node_w->_M_color = _S_rb_tree_black;
                    node_x_parent->_M_color = _S_rb_tree_red;
                    _Rb_tree_rotate_right(node_x_parent, __root);
                    node_w = node_x_parent->_M_left;
                }
                if ((node_w->_M_right == 0 || node_w->_M_right->_M_color == _S_rb_tree_black) &&
                    (node_w->_M_left == 0 || node_w->_M_left->_M_color == _S_rb_tree_black))
                {
                    node_w->_M_color = _S_rb_tree_red;
                    node_x = node_x_parent;
                    node_x_parent = node_x_parent->_M_parent;
                }
                else
                {
                    if (node_w->_M_left == 0 || node_w->_M_left->_M_color == _S_rb_tree_black)
                    {
                        if (node_w->_M_right)
                        {
                            node_w->_M_right->_M_color = _S_rb_tree_black;
                        }
                        node_w->_M_color = _S_rb_tree_red;
                        _Rb_tree_rotate_left(node_w, __root);
                        node_w = node_x_parent->_M_left;
                    }

                    node_w->_M_color = node_x_parent->_M_color;
                    node_x_parent->_M_color = _S_rb_tree_black;

                    if (node_w->_M_left)
                    {
                        node_w->_M_left->_M_color = _S_rb_tree_black;
                    }
                    _Rb_tree_rotate_right(node_x_parent, __root);
                    break;
                }
            }
        }
        if (node_x)
        {
            node_x->_M_color = _S_rb_tree_black;
        }
    }
    return node_y;
}


发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

Copyright © 1999-2019, lu16.com, All Rights Reserved