STL RB Tree(红黑树)分析

当我2014年上半年看内核代码的时候,进程调度用的就是RB  Tree,而现在分析STL源码的时候发现Set和Map也使用了这个数据结构,说明了RBTree的使用时如此的广泛,所以我花了两天时间看了这,分三部分来说明,首先我要说明下红黑树的基本概念,然后说明下STL中的RB Tree的迭代器,最后说下STL中RB Tree容器的实现。

一、红黑树的基本概念

红黑树是平衡二叉搜索树的一种(平衡二叉搜索树中又有AVL Tree),满足二叉搜索树的条件外,还应买足下面的4个条件

1) 每个节点不是红色就是黑色;

2) 根节点是黑色;

3)如果节点是红,那么子节点为黑色;(所以新增节点的父节点为黑色)

4)任一节点到NULL(树尾端)的任何路径,所含的黑节点数必须相同;(所以新增节点为红色)

那么如果按照二叉搜索树的规则插入节点,发现未能符合上面的要求,就得调整颜色并旋转树形。

下面分情况讨论才,插入节点后,发现未能符合要求的几种情况,以及我怎样去调整颜色和旋转树形。

在上图的红黑树种,我们插入四个节点3、8、35、75,插入后首先肯定是红色的,在上图的情况中,这四个插入操作都会违反条件三(红色节点的子节点为黑色),上面的四个点代表了四中情况,而这个图也是很具有代表性的,下面我们就来分情况分析下:

情况一:

插入节点3,如下图所示:

节点3的伯父节点是黑色节点(这里是NULL的话就算作黑色),节点3为外侧插入,这种情况下,需要做一次右旋:

这里的右旋是将爷爷节点下降一层,将父节点上升一层,因为父节点是红色,根据条件三,红色节点的子节点为黑色,所以讲父节点的颜色改为黑色,根据保证条件4,将下降的爷爷节点颜色改为红色,为了满足二叉搜索树的条件,即左子树的值小于/大于右字树的值,所以将父节点的左子树移动给爷爷节点的左子树。

情况二,插入节点8,8的伯父节点(也可以说是叔叔节点)是黑色的(空算作是黑色),插入为内侧插入:

所做的旋转和调色如上图所示,将8上调5下调之后,将8的颜色调为黑色,以满足条件3,将8的左子树移交给5的右子树以满足二叉搜索树的条件,然后再将爷爷节点调整为红色,调整后为上图第二个所示,然后再做一次右旋(是为了减少左右子树的高度差)。

情况三,插入节点75,那么该节点,伯父节点为红色,且插入为外侧插入:

此时爷爷节点85无右旋点,右旋一次以后OK,因为此时曾祖父节点为黑色,所以OK;

情况四,插入节点值为35的节点,和情况三的不同点是调整后,曾祖父节点为红色,那么就得继续往上做同样的旋转和颜色调整,直到不再有父子连续为红色的为止看,如下图所示:

OK,关于如何插入节点已经集中情况已经说完了,那么如何用代码实现则在下面继续说明。

二、红黑树迭代器的实现

这里我先直接将代码贴上来:

typedef bool __rb_tree_color_type;
typedef __rb_tree_color_type __rb_tree_red   = false;
typedef __rb_tree_color_type __rb_tree_black = true;

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;
	base_ptr left;
	base_ptr right;

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

	static base_ptr maximum(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;
};

struct  __rb_tree_base_iterator
{
	typedef	__rb_tree_node_base::base_ptr	        base_ptr;
	typedef	bidirectional_iterator_tag		iterator_category;
	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;
			}
			if (node->right != y)
				node = y;
		}
	}

	void decrement() {
		if( node->color == __rb_tree_red && node->parent->parent == node) {
			node = node->left;
		}
		else if (node->left != 0) {
			node = node->left;
			while ( node->right != 0) {
				node = node->right;
			}
		}
		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
{
	typedef Value  	value_type;
	typedef Ref 	referece;
	typedef Ptr 	pointer;

	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_offset = x ;}
	__rb_tree_iterator(const iterator &it) { node = it.node; }

	referece operator*()  const { return link_type(node)->value_field ;}
	referece 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--() {
		self tmp = *this;
		decrement();
		return tmp;
	}

};

这里我要分析下函数increment(),decrement()和increment是类似的,所以这里我只说下increment

	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;
			}
			if (node->right != y)
				node = y;
		}
	}

这里increment是为了将node指向下一个大于它的node,node的右子树节点的值是都大于node的,而右子树中最小的节点是右子树最左下的节点;

右子树为空的话,那么只能上溯,如果node是node->parent的右孩子的话,那么node是大于node->parent的值的,相反,是node->parent的左孩子的话,是小于parent的,那么下一个大于node的是node所处的左子树的父节点。

(最后一个判断是为了处理RB-Tree根节点和header之间的特殊关系)

三、红黑树的实现

实现代码比较长,代码逻辑并不难,对照上面的例子分析代码,并不难,这里我只说下函数insert_unique,虽然逻辑也不难;

数据成员header的parent是root,left是leftmost,right是rightmost,这是实现上的技巧

template <class Key , class Value , class KeyOfValue , class Compare , class Alloc = alloc>
class rb_tree
{
protected:
	typedef void* 	void_pointer;
	typedef __rb_tree_node_base 	*base_ptr;
	typedef __rb_tree_node<Value>	rb_tree_node;
	typedef simple_alloc<rb_tree_node , Alloc>	rb_tree_node_allocator;
	typedef __rb_tree_color_type color_type;

public:
	typedef Key 	key_type;
	typedef Value 	value_type;
	typedef const value_type* 	const_iterator;
	typedef value_type&			reference;
	typedef const value_type&	const_reference;
	typedef rb_tree_node*		link_type;
	typedef size_t 				size_type;
	typedef ptrdiff_t			difference_type;

public:
	link_type get_node() {
		return rb_tree_node::allocate();
	}

	void put_node(link_type p) {
		rb_tree_node::deallocate();
	}

	link_type create_node(const value_type& x) {
		link_type tmp = get_node();
		construct(&tmp->value_field , x)
		return tmp;
	}

	link_type clone_node(link_type x) {
		link_type tmp = create_node(x->value_field);
		tmp->color = x->color;
		tmp->left 	= 0;
		tmp->right 	= 0;
		return tmp;
	}

	void destroy_node(link_type p) {
		destroy(&p->value_field);
		put_node(p);
	}

protected:
	size_type	node_count;
	link_type	header;
	Compare		key_compare;

	link_type&	root() const { return (link_type&) header->parent; }
	link_type&	leftmost() const { return (link_type&) header->left; }
	link_type&	rightmost() const { return (link_type&) header->right;}

	static	link_type& 	left(link_type x)		{	return (link_type&) x->left; 	}
	static	link_type& 	right(link_type x)		{	return (link_type&)	x->right;	}
	static	link_type& 	parent(link_type x)	 	{	return (link_type&)	x->parent;	}
	static	reference 	value(link_type x)		{	return	x->value_field;	}
	static	const Key&	key(link_type x)		{	return	KeyOfValue() (value(x));	}
	static	color_type& color(link_type x)		{	return	(color_type&) (x->color);	}

	static	link_type& 	left(base_ptr x)	 	{	return (link_type&) x->left; 	}
	static	link_type& 	right(base_ptr x)	 	{	return (link_type&)	x->right;	}
	static	link_type& 	parent(base_ptr x)	 	{	return (link_type&)	x->parent;	}
	static	reference 	value(base_ptr x)		{	return	x->value_field;			}
	static	const Key&	key(base_ptr x)			{	return	KeyOfValue() (value(x));	}
	static	color_type& color(base_ptr x)		{	return	(color_type&) (x->color);	}

	static	link_type minimum(link_type x) {
		return (link_type) __rb_tree_node_base::minimum(x);
	}

	static	link_type maximum(link_type x) {
		return (link_type) __rb_tree_node_base::maximum(x);
	}

public:
	typedef	__rb_tree_iterator<value_type , reference , pointer>	iterator;

private:
	iterator 	__insert(base_ptr x , base_ptr y, const value_type& v);
	link_type	__copy(link_type x  , link_type p);
	void 		__erase(link_type	x);
	void init() {
		header = get_node();
		color(header) = __rb_tree_red;

		root() = 0;
		leftmost() 	= header;
		rightmost()	= header;
	}

public:
	rb_tree(const Compare& comp = Compare()): node_count(0) , key_compare(comp) {
		init();
	}

	~rb_tree() {
		clear();
		put_node(header);
	}

	rb_tree<Key , Value , KeyOfValue , Compare , Alloc>&	operator= (const rb_tree<Key , Value , KeyOfValue , Compare , Alloc>& x);

	Compare 	key_comp() const 	{	return 	key_compare; }
	iterator 	begin()			 	{	return 	leftmost();  }
	iterator 	end()			 	{	return	header; }
	bool 		empty()			 	{	return	node_count == 0; }
	size_type	size()	const 	 	{	return 	node_count;	}
	size_type	max_size()	const 	{	return	size_type(-1);	}

public:
	pair<iterator , bool> inset_unique(const value_type& x);
	iterator insert_equal(const value_type& x);
};

template <class Key , class Value , class KeyOfValue , class Compare , class Alloc = alloc>
typename rb_tree<Key , Value , KeyOfValue , Compare , Alloc>::iterator
rb_tree<Key , Value , KeyOfValue , Compare , Alloc>::insert_equal(const Value& x)
{
	link_type y = header;
	link_type x = root();
	while ( x != 0 ) {
		y = x;
		x = key_compare(KeyOfValue()(v) , key(x)) ? left(x) : right(x);
	}
	return __insert(x , y ,v);
}

template <class Key , class Value , class KeyOfValue , class Compare , class Alloc = alloc>

template <class Key , class Value , class KeyOfValue , class Compare , class Alloc = alloc>
typename rb_tree<Key , Value , KeyOfValue , Compare , Alloc>::iterator
rb_tree<Key , Value , KeyOfValue , Compare , Alloc>::__insert(base_ptr x_ , base_ptr y_ , const Value& v)
{
	link_type	x = (link_type) x_;
	link_type	y = (link_type)	y_;
	link_type	z;

	if ( y == header || x != 0 || key_compare(KeyOfValue()(v) , key(v))) {
		z = create_node(v);
		left(y) = z;
		if ( y == header) {
			root() = z;
			rightmost() = z;
		}
		else if (y == leftmost())
			leftmost = z;
	}
	else {
		z = create_node(v);
		right(y) = z;
		if ( y == rightmost() )
			rightmost() = z;
	}
	parent(z) 	= y;
	left(z)		= 0;
	right(z)	= 0;

	__rb_tree_rebalance(z , header->parent);
	++node_count;
	return iterator(z);
}

inline void __rb_tree_rebalance( __rb_tree_node_base* x  , __rb_tree_node_base* &root)
{
	x->color = __rb_tree_red;
	while ( x != root && x->parent->color == __rb_tree_red ) {
		if ( x->parent == x->parent->parent->left) {
			__rb_tree_node_base* y = x->parent->parent->right;
			if ( y && y->color == __rb_tree_red ) {
				x->parent->color = __rb_tree_black;
				y->color = __rb_tree_black;
				x->parent->parent->color = __rb_tree_red;
				x = x->parent->parent;
			}
			else {
				if ( x == x->parent->right) {
					x = x->parent;
					__rb_tree_rotate_left (x , root);
				}
				x->parent->color = __rb_tree_black;
				x->parent->parent->color = __rb_tree_red;
				__rb_tree_rotate_right (x->parent->parent , root);
			}
		}
		else {
			__rb_tree_node_base* y = x->parent->parent->right;
			if ( y && y->color == __rb_tree_red) {
				x->parent->color = __rb_tree_black;
				y->color = __rb_tree_black;
				x->parent->parent->color = __rb_tree_red;
			}
			else {
				if (x == x->parent->left ) {
					x = x->parent;
					__rb_tree_rotate_right(x , root);
				}
				x->parent->color = __rb_tree_black;
				x->parent->parent->color = __rb_tree_red;
				__rb_tree_rotate_left(x->parent->parent , root);
			}
		}
	}

	root->color = __rb_tree_black;
}

inline void __rb_tree_rotate_left(__rb_tree_node_base* x , __rb_tree_node_base* &root)
{
	__rb_tree_node_base* y = x->right;
	x->right = y->left;
	if (y->left != 0)
		y->left->parent = x;

	if (x == root)
		root = y;
	else if ( x == x->parent->left )
		x->parent->left = y;
	else
		x->parent->right = y;
	y->left = x;
	x->parent = y;
}

inline void __rb_tree_rotate_rigth(__rb_tree_node_base* x , __rb_tree_node_base* &root)
{
	__rb_tree_node_base* y = x->left;
	x->left = y->right;
	if (y->right != 0)
		y->right->parent = x;

	if (x == root)
		root = y;
	else if ( x == x->parent->left )
		x->parent->left = y;
	else
		x->parent->right = y;
	y->right = x;
	x->parent = y;
}

至于函数insert_unique,是保证插入的键值不允许重复

typename rb_tree<Key , Value , KeyOfValue , Compare , Alloc>::iterator
rb_tree<Key , Value , KeyOfValue , Compare , Alloc>::insert_unique(const Value& x)
{
	link_type 	y = header;
	link_type	x = root();
	bool	comp = true;

	while ( x != 0 ) {  //从根节点开始 往下寻找适当的插入点
		y = x ;
		comp = key_compare(KeyOfValue()(v) , key(x));
		x = comp ? left(x) : right(x); //遇大则往左,小于等于则往右
	}
       //离开之后, y即为插入点之父节点,此时它必为叶节点

        iterator j = iterator(y);
	if (comp) { //如果离开while循环的时候,comp是真,说明是插入点是y的左孩子
		if (j == begin()) { //插入点父节点是最左节点,此时,不会有重复键值
			return pair<iterator , bool> (__insert(x , y ,v) , true);
		}
		else
			-- j;
	}

	if ( key_compare (key(j.node) , KeyOfValue()(v)))
		return	pair<iterator , bool> (__insert(x , y ,v) , true);

	return (pair<iterator,bool> , false);
}

插入点父节点不是最左边的节点的话,--j,是将j指向比父节点小的上一个节点,和v的键值比较,不相等说明是没有重复,因为插入点是左孩子,必然是小于父节点的,那么和比父节点小点的节点比较(v肯定是大于等于该值的),如果不是等于,则插入;

另外如果插入点是父节点y的右孩子的话,右孩子是大于等于y的,那么和y比较大小,如果不等于则插入。

这里呢,我只备注了下我看代码的时候让我迷惑的那些代码,如果哪有说的不对的地方,欢迎指正,谢谢 O(∩_∩)O哈哈~

时间: 2024-08-09 09:56:39

STL RB Tree(红黑树)分析的相关文章

红黑树分析,看了都说好

红黑树简介 红黑树是一种自平衡的二叉查找树,是一种高效的查找树.它是由 Rudolf Bayer 于1978年发明,在当时被称为对称二叉 B 树(symmetric binary B-trees).后来,在1978年被 Leo J. Guibas 和 Robert Sedgewick 修改为如今的红黑树.红黑树具有良好的效率,它可在 O(logN) 时间内完成查找.增加.删除等操作.因此,红黑树在业界应用很广泛,比如 Java 中的 TreeMap,JDK 1.8 中的 HashMap.C++

Red-Black Tree 红黑树

如果viewer对Red-Black Tree先前没有任何了解,可以看看先搞定AVL树,因为AVL树比红黑树要简单的多 涉及同样的rotate技巧,搞懂了AVL树再和红黑大战三百回合绝对轻松的多. (我见过讲AVL最好的教程就是 Mark Allen Weiss 写的<数据结构与算法分析 C语言描述>里面讲AVL树的部分) AVL树的C语言实现: http://blog.csdn.net/cinmyheart/article/details/20527037 Here we go ~ 笔记贴-

死磕 java集合之TreeMap源码分析(三)- 内含红黑树分析全过程

欢迎关注我的公众号"彤哥读源码",查看更多源码系列文章, 与彤哥一起畅游源码的海洋. 删除元素 删除元素本身比较简单,就是采用二叉树的删除规则. (1)如果删除的位置有两个叶子节点,则从其右子树中取最小的元素放到删除的位置,然后把删除位置移到替代元素的位置,进入下一步. (2)如果删除的位置只有一个叶子节点(有可能是经过第一步转换后的删除位置),则把那个叶子节点作为替代元素,放到删除的位置,然后把这个叶子节点删除. (3)如果删除的位置没有叶子节点,则直接把这个删除位置的元素删除即可.

《红黑树》学习心得

R-B Tree简介 R-B Tree,全称是Red-Black Tree,又称为"红黑树",它一种特殊的二叉查找树.红黑树的每个节点上都有存储位表示节点的颜色,可以是红(Red)或黑(Black). 红黑树的特性:(1)每个节点或者是黑色,或者是红色.(2)根节点是黑色.(3)每个叶子节点(NIL)是黑色. [注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!](4)如果一个节点是红色的,则它的子节点必须是黑色的.(5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的

数据结构--树--红黑树

R-B Tree简介 R-B Tree,全称是Red-Black Tree,又称为“红黑树”,它一种特殊的二叉查找树.红黑树的每个节点上都有存储位表示节点的颜色,可以是红(Red)或黑(Black). 红黑树的特性:(1)每个节点或者是黑色,或者是红色.(2)根节点是黑色.(3)每个叶子节点(NIL)是黑色. [注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!](4)如果一个节点是红色的,则它的子节点必须是黑色的.(5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点.

数据结构(三):非线性逻辑结构-特殊的二叉树结构:堆、哈夫曼树、二叉搜索树、平衡二叉搜索树、红黑树、线索二叉树

在上一篇数据结构的博文<数据结构(三):非线性逻辑结构-二叉树>中已经对二叉树的概念.遍历等基本的概念和操作进行了介绍.本篇博文主要介绍几个特殊的二叉树,堆.哈夫曼树.二叉搜索树.平衡二叉搜索树.红黑树.线索二叉树,它们在解决实际问题中有着非常重要的应用.本文主要从概念和一些基本操作上进行分类和总结. 一.概念总揽 (1) 堆 堆(heap order)是一种特殊的表,如果将它看做是一颗完全二叉树的层次序列,那么它具有如下的性质:每个节点的值都不大于其孩子的值,或每个节点的值都不小于其孩子的值

数据结构-红黑树详解

介绍: 红黑树(Red Black Tree) 是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组. 它是在1972年由Rudolf Bayer发明的,当时被称为平衡二叉B树(symmetric binary B-trees).后来,在1978年被 Leo J. Guibas 和 Robert Sedgewick 修改为如今的"红黑树". 红黑树和AVL树类似,都是在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能. 它虽然

关联容器(底层机制) — 红黑树

set.map.multiset.multimap四种关联式容器的内部都是由红黑树实现的.在STL中红黑树是一个不给外界使用的独立容器.既然是容器,那么就会分配内存空间(节点),内部也会存在迭代器.关于红黑树的一些性质,可以参考"数据结构"中的笔记,这里只记录STL中的红黑树是如何实现的. 和slist一样,红黑树的节点和迭代器均采用了双层结构: 节点:__rb_tree_node继承自__rb_tree_node_base 迭代器:__rb_tree_iterator继承自__rb_

红黑树并没有我们想象的那么难(下)

SGI STL map 实现概述 根据上一节的红黑树分析, 结合 sgi stl map 的实现, 看看红黑树的源码是如何实现的. 以下主要以代码的注释为主. sgi stl map 底层实现是 _Rb_tree类, 为了方便管理, _Rb_tree 内置了 _M_header, 用于记录红黑树中的根节点, 最小节点和最大节点. 在插入删除中都会对其进行维护. 找到一副美艳的图片: 我只会展开插入和删除的代码. _Rb_tree 有 insert_unique() 和 insert_equal(