红黑树性质、插入节点和rb_tree容器



1. RB-tree 性质

RB-tree(红黑树)是一种颇具历史并被广泛运用的平衡二叉树。相比于另一种平衡二叉树AVL-tree,其对平衡性规则的要求要宽松一些,且插入删除操作更便于控制,但是搜寻平均效率几乎相等。所谓RB-tree,不仅是一个二叉搜索树,而且必须满足以下规则:

  • 1. 每个节点不是红色就是黑色(图中深色底纹代表黑色,浅色底纹代表红色);
  • 2. 根节点为黑色;
  • 3. 如果节点为红色,其子节点必须为黑色;
  • 4. 任一节点至NULL(树尾端)的任何路径,所含之黑色节点数必须相同;

总结就是:

  • 红黑树的性质是每条路径的黑色节点数目相同
  • 红黑树保证最长路径不超过最短路径的二倍,因而近似平衡
    (对于红黑树中,最长路径是严格红黑相间的路径,因为红色和黑色只能相间排列,且以红色结尾,所以红色和黑色数量相等,又最短路径是全黑节点路径,且每个路径黑色数量相等,所以有此结论)


2. RB-tree 插入

根据规则4,新增节点必须为红色;根据规则3,新增节点之父节点必须为黑色。当新节点根据二叉搜索树的规则到达其插入点,却未能符合上述条件时,就必须调整颜色并旋转树形。见下图:

对于红黑树节点的插入,也包括AVL树中的左旋、右旋、左右旋、右左旋调整情形,但是旋转之后还要考虑颜色的排布符合规则要求。具体步骤如下:

  • 根节点为NULL,直接插入新节点并将其颜色置为黑色;
  • 根节点不为NULL,找到要插入新节点的位置;
  • 插入新节点,插入的新节点默认颜色为红色
  • 判断新插入节点对全树颜色的影响,更新调整颜色;

具体的插入过程推荐点击如下的演示器链接自己试验一下!!!


共分为3大类,9种小的情况(更改颜色1,单旋4,双旋4排布),红黑树动画演示器


( 一 ) (调整颜色) c u r cur cur 为红, p a r e n t parent parent 为红, p P a r e n t pParent pParent 为黑, u n c l e uncle uncle 存在且为红色

p a r e n t parent parent u n c l e uncle uncle 节点改为黑色, p P a r e n t pParent pParent 改为红色,再将 p P a r e n t = c u r pParent=cur pParent=cur,继续向上调整(情形1)


( 二 ) (单旋) c u r cur cur 为红, p a r e n t parent parent 为红, p P a r e n t pParent pParent 为黑, u n c l e uncle uncle 不存在或为黑色


2.1 p a r e n t parent parent p P a r e n t pParent pParent 的左孩子, c u r cur cur p a r e n t parent parent 的左孩子,则进行右单旋:

2.1.1 u n c l e uncle uncle 不存在(右单旋)(情形2)

2.1.2 u n c l e uncle uncle 存在且为黑色(右单旋)(情形3)


2.2 p a r e n t parent parent p P a r e n t pParent pParent 的右孩子, c u r cur cur p a r e n t parent parent 的右孩子,则进行左单旋:

2.2.1 u n c l e uncle uncle 不存在(左单旋)(情形4)

2.2.2 u n c l e uncle uncle 存在且为黑色(左单旋)(情形5)


( 三 ) (双旋) c u r cur cur 为红, p a r e n t parent parent 为红, p P a r e n t pParent pParent 为黑, u n c l e uncle uncle 不存在或为黑色


3.1 p a r e n t parent parent p P a r e n t pParent pParent 的左孩子, c u r cur cur p a r e n t parent parent 的右孩子,则对 p a r e n t parent parent 进行左单旋, 转换为2.2:

3.1.1 u n c l e uncle uncle 不存在(左右旋)(情形6)

3.1.2 u n c l e uncle uncle 存在且为黑色(左右旋)(情形7)


3.2 p a r e n t parent parent p P a r e n t pParent pParent 的右孩子, c u r cur cur p a r e n t parent parent 的左孩子,则对 p a r e n t parent parent 进行右单旋, 转换为2.1:

3.2.1 u n c l e uncle uncle 不存在(右左旋)(情形8)

3.2.2 u n c l e uncle uncle 存在且为黑色(右左旋)(情形9)



3. STL非公开rb_tree容器

图1 rb_tree数据结构
图2 __rb_tree_node构成

具体源码可查看 SGI STL的rb_tree浅析

3.1 rb_tree 数据结构

rb_tree容器提供遍历操作及iterators。按照正常规则(++iter实际是二叉树的先序遍历),便能获得排序状态(sorted)。以以上红黑树为例,iter进行自加遍历之后得到的序列为 [ 5 , 6 , 7 , 8 , 10 , 11 , 12 , 13 , 15 ] [5, 6, 7, 8, 10, 11, 12, 13, 15] [5,6,7,8,10,11,12,13,15]

在使用rb_tree时不应使用其iterator改变元素值,尽管在实际编程实现上STL并没有阻止变值操作。主要原因是rb_tree作为set和map容器的底层容器,其中map<key, value>是允许对元素(即__rb_tree_node中的value )中的data进行改变,而对key(排序依据)是不能够改变的。

rb_tree提供两种insertion操作:insert_unique()insert_equal()

  • unique表示节点的key一定在整个红黑树中独一无二,否则插入失败(并不会造成程序问题,count()结果为二元制0或1),这种insertion对应set/map;
  • equal表示节点的key可重复,这种insertion对应multiset/multimap;
//GNU 2.9版本, GNU 4.9的实现是为了参照handle\body规范,有些得不偿失?
template<class Key, class Value, class KeyOfValue, class Compare, class Alloc = alloc>
//Key:键的类型,Value:值的类型,其中key和data合成value
//KeyOfValue:如何取出key,如gnu C中的identity<int>,是一种仿函数
//Compare:key的大小比较准则,是一个函数对象function object
class rb_tree {
protected:
	typedef __rb_tree_node<Value> rb_tree_node;
	...
public:
	typedef rb_tree_node* link_type;
	...
protected:
	//rb_tree以三个成员来表示自己
	size_type node_count; //rb_tree的大小,也即节点数量
	link_type header;     //连接root的dummy节点,如上图所示
	Compare   key_compare //key的大小比较准则,是一个函数function object,如less<T>;或仿函数functor
	//类数据就这个三个。
    //4 + 4 + 1 = 9 --->12	4字节对齐
    //仿函数为空,默认都是1字节。 

public:
	iterator begin() { return leftmost(); }//RB树的起头为最左节点(指向数值最小的节点),类里面含有一个起始迭代器和结束结束迭代器,用来管理树
	iterator end() { return header; }//RB树的终点,最右边节点(指向数值最大的节点),头结点使用,使其实现左闭右开的用法。

    //将x插入到RB-tree中(保持节点key值独一无二),返回一个pair对象,里面存储对应迭代器和成功与否。
  	pair<iterator,bool> insert_unique(const value_type& x);

    //key值可以重复
    //将x插入到RB-tree中(允许节点值重复),返回插入的新节点的迭代器。
 	iterator insert_equal(const value_type& x);
 ...
};

3.2 __rb_node 设计实现

节点设计分为两层,有一底层base实现,__rb_tree_node public继承该base。__rb_tree_node的设计源码如下:

//SGI STL
typedef bool __rb_tree_color_type;
const __rb_tree_color_type __rb_tree_red = false; //红色为0
const __rb_tree_color_type __rb_tree_black = true; //黑色为1

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

	color_type color; //节点颜色,非红即黑
	base_ptr   parent; //RB树的许多操作需要知道父节点,
	//这里可以想到树中两节点最低公共祖先问题中,节点有父节点指针情形,退化到两个链表的第一个交点问题
	base_ptr   left;   //指向左孩子
	base_ptr   right;  //指向右孩子
	
	static base_ptr minimum(base_ptr x) { //一直向左走,就会找到最小值,BST的性质
		while(x->left != 0) x = x->left; 
		return x;
	}
	...
	//maximum实现略
};

template<class Value>
struct __rb_tree_node : public __rb_tree_node_base
{
	typedef __rb_tree_node<Value>* link_type;
	Value value_field; //节点值
	//一个实现见图2 __rb_tree_node构成
};

3.3 __rb_tree_iterator 设计实现

rb_tree的迭代器设计也设计为两层,base_iteratoriterator。RB-tree迭代器属于双向迭代器,但不具备随即定位能力,其提领操作与list十分近似,较为特殊的是其前进和后退操作。迭代器的operator++()调用了基层迭代器的increment(),后退操作调用了decrement()。前进或后退的举止完全依据二叉搜索树的节点排列法则,加上实现的技巧。

//rb树的迭代器
//注意迭代器要实现的5个特性:value_type, iterator_category, difference_type, reference, pointer
struct __rb_tree_base_iterator
{
    typedef __rb_tree_node_base::base_ptr base_ptr;
    typedef bidirectional_iterator_tag iterator_category; //(4)
    typedef ptrdiff_t difference_type;                    //(5)
    base_ptr node;

    //指向下一个节点(++操作),找到比node大的第一个节点
    void increment()
    {
        if(node->right != 0){   //若当前节点的右树不空,则其直接前驱就在其右树部分
            node = node->right;   //在node的右树中找出最左的节点就是node的直接前驱
            while(node->left != 0){
                node = node->left;
            }
        }else{    //node的右树为空,则进行上溯查找node的父节点,直到找到node的直接前驱
            base_ptr y = node->parent;
            while(node == y->right){
                node = y;
                y = y->parent;
            }

            if(node->right != y){
                node = y;
            }
        }
    }

    //指向上一个节点(--操作),找到比node小的第一个节点
    void decrement()
    {
        if(node->color == __rb_tree_red && node->parent->parent == node){  //node为header
            node = node->right;
        }else if(node->left != 0){   //若node的左树不为空,就在左树中找到最右侧的节点
            base_ptr y = node->left;
            while(y->right != 0){
                y = y->right;
            }

            node = y;
        }else{    //node的左树为空,则上溯,查找node的父节点,找到node的直接前驱
            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
{
    typedef Value value_type;    //(1)
    typedef Ref reference;       //(2)
    typedef Ptr pointer;         //(3)
    typedef __rb_tree_iterator<Value, Value& , Value*>    iterator;
    typedef __rb_tree_iterator<Value, const Value&, const Value*> const_iterator;

    typedef __rb_tree_iterator<Value, Ref, Ptr>   self;
    typedef __rb_tree_node<Value>*   link_type;

    __rb_tree_iterator()
    {
    }
    __rb_tree_iterator(link_type x)
    {
        node = x;
    }
    __rb_tree_iterator(const iterator& it)
    {
        node = it.node;
    }

    reference operator*()const
    {
        return link_type(node)->value_field;
    }
    pointer operator->()const
    {
        return &(operator*());
    }

    self& operator++()
    {
        increment();
        return *this;
    }
    self operator++(int)
    {
        self tmp = *this;
        increment();
        return tmp;
    }

    self& operator--()
    {
        decrement();
        return *this;
    }
    self operator--(int)
    {
        self tmp = *this;
        decrement();
        return tmp;
    }
};

rb_tree的无论是节点还是迭代器都是以struct定义的原因是: struct的所有成员都是public,所以其所有成员都可被外界自由取用。

下图是迭代器和节点之间的对应关系:
在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值