STL源码剖析学习十:红黑树的实现

STL源码剖析学习十:红黑树的实现

 

RB-Tree的节点设计:
节点设计分为两层:

struct __rb_tree_node_base
{
    typedef __rb_tree_color_type color_type;
    typedef __rb_tree_node_base* base_ptr;

    color_typr color;
    base_ptr parent, left, right;

    static base_ptr minimun(base_ptr x)
    {
        while (x->left != 0)
            x = x->left;
        return x;
    }
    static base_ptr maximun(base_ptr x)
    {
        while (x->right != 0)
            x = x->right;
        return x;
    }
};

 

template<class value>
struct __rb_tree_node:public __rb_tree_node_base
{
    typedef __rb_tree_node<value>* link_type;
    value value_field;//关键是把节点的值独立出来
};

 

RB-Tree的迭代器:
也分两层,和slist的设计比较相似,比较特殊的是前进和后退的操作。

struct __rb_tree_base_iterator
{
    typedef __rb_tree_node_base::base_ptr base_ptr;
    typedef bidirectional_iterator_tag iterator_catogory;
    typedef ptrdiff_t difference_type;
    base_ptr node;
    
    void increment()
    {
        if(node->right != 0)
        {
            node = node->right;
            while(node->left != 0)
                node = node->left;
        }
        //如果有右子结点,就找到其右子结点的最左子节点
        else
        {
            base_ptr y = node->parent;
            //如果没有右子结点,找出父节点,如果现行节点的父节点也是右子结点,就继续上溯
            //直到现行节点不是右子结点为止
            while(node == y->right)
            {
                node = y;
                y = y->parent;
            }
            //若此时右子结点不等于此时的父节点,此时的父节点即为解答(为了应付特殊情况:当前节点为根节点)
            //否则此时的node为解答
            if(node->right != y)
                node = y;
        }
    }
    void decrement()
    {
        if(node->color == __rb_tree_red && node->parent->parent == node)
            node == node->right;
        //如果本节点为红且父节点的父节点为自己,说明该节点为header,前一节点为该节点的右子结点
        //header的右子结点为该树的最右子结点,指向max节点
        else if (node->left != 0)
        {
            base_ptr y = node->left;
            while(y->right != 0)
                y = y->right;
            node = y;
        }
        //如果有左子节点,就取左子节点在最右子结点
        else
        {
            //找出父节点,如果本节点也为左子节点,则继续上溯直到本节点不为左子节点
            base_ptr y = node->parent;
            while(node == y->left)
            {
                node = y;
                y = y->parent;
            }
            node = y;//此时的父节点即为所求
        }
    }
}

 

正规迭代器:
继承了基础迭代器

template <class value, class ref, class ptr>
struct __rb_tree_iterator:public __rb_tree_base_iterator

 


RB-tree 的元素操作

insert_equal()//该函数允许有重复值插入
{
    从根节点开始向下寻找适当的插入点
    如果遇到大则往左,遇到小则往右
    __insert(x, y, v)//x为新插入点,y为插入点的父节点,v为值
}

 

insert_unique()
{
    while
    {
        查找插入点:
        从根节点开始向下寻找适当的插入点
        如果遇到大则往左,遇到小与等于则往右
    }
    查找完成后y指向插入点的父节点(此时其必为叶节点)
    如果离开循环时遇到大,则将其插于左侧(默认大的放左边,小的放右边)
    如果插入点的父节点为最左节点,则直接插入
    return __insert(x, y, v)
    否则看父节点的前驱结点的键是否和要插入的键相同
    (如果要出现相同的键,只可能是该节点的直接前驱)
    如果不相等 则插入
    return __insert(x, y, v)
    到此则说明有相等值,则不插入
}

 

真正的插入程序

__insert()
{
    根据插入点和插入点的父节点
    调节指针,并且判断是否为最右或者最左节点,调整leftmost和rightmost的指向
    调整插入节点的指针的指向
    __rb_tree_rebalance(z, header->parent)//一为新增节点,二为root
    ++node_count;
    return iterator(z);
    //返回新增节点的迭代器
}

 

__rb_tree_rebalance()
根据之前所说红黑树插入后调整树形的规则进行调整

里面用到两个全局函数__rb_tree_rotate_left/right 左旋/右旋调整

 

set、map、multimap、multiset

有了都是在红黑树的基础上实现的,功能和接口都比较简单

set的特性是,所有元素都会根据键值自动排序,不允许有键值和实值,两者等价,不允许有两个相同的键值

set不能通过迭代器改变元素的值,因为涉及到排列规则,要改变值只能先删除后插入

set<T>::iterator被定义为底层的const_iterator杜绝写入操作

set拥有某些与list相同的性质:当对元素进行插入或者删除操作时,之前的迭代器都不会失效。

 

map和set的区别是map区分键值和实值
multimap和multiset的特点是允许有相同键值的存在
因为其底层调用为insertt_equal而非insert_unique

 

 


STL源码剖析这块内容里没有涉及到红黑树删除操作的实现
下面看了算法导论和其他一些资料后补上:

红黑树的删除类似于二叉查找树的删除,实际上删除的节点是该节点的直接前驱。
并且把该节点直接前驱的值赋给要删除的节点的位置。


分情况讨论:
1.如果该节点是红色节点,直接染黑。
2.如果该节点为黑色节点,且为根节点,直接删除
3.如果该节点为黑且兄弟w为红,则p必为黑,把p和w改色,然后单旋转解决
4.兄弟为黑且兄弟2儿子都为黑:
把当前节点和兄弟节点中都抽出一层黑色来给父节点,把父节点当做当前节点,继续进入算法。
5.当前为黑,兄弟节点为黑,兄弟左子节点为红,右子结点为黑:
把兄弟染红,把左子节点染黑,以兄弟节点为支点旋转,重新进入算法(转换为下一种情况)
6.兄弟为黑,兄弟右子结点为红,兄弟左子节点任意:
把兄弟节点染成父节点的颜色,把父节点染成黑色,兄弟节点的右子结点染黑,当前节点的父节点为支点旋转即可

(图就不画了,懒╮(╯▽╰)╭)

posted @ 2012-04-24 16:25  w0w0  阅读(711)  评论(0编辑  收藏  举报