`
hyshucom
  • 浏览: 808375 次
文章分类
社区版块
存档分类
最新评论

STL的红与黑--rb_tree

 
阅读更多

红黑树,作为一种广泛使用的数据结构,我想大家应该都不会陌生。

谈到红黑树的用途,最广为人知的应该就是红黑树在C++ STL中的应用了,在set, multiset, map, multimap等中,都应用了红黑树。但是,rb_tree本身并不开放给外界使用。

今天,我将介绍,STL源码中,红黑树的具体实现(因为篇幅所限,这里不包括删除操作)。

因为文章的主要目的是分析STL中的源码,所以对于红黑树的具体实现并不展开,这类文章,大家可以到网上查找。这里推荐wiki上的一篇。

首先,我还是简要的介绍一下红黑树的构造、维护思想。

红黑树:

红黑树是二叉查找树(BST),同时也是一种平衡二叉查找树。

虽然它的平衡不如AVL树,但是,它和AVL树一样都对插入时间、删除时间和查找时间提供了最好可能的最坏情况担保。

它可以在O(logn)时间内做查找,插入和删除,这里的n是树中元素的数目。

红黑树性质
红黑树是每个节点都带有颜色属性的二叉查找树,颜色为红色或黑色。在二叉查找树强制一般要求以外,对于任何有效的红黑树我们增加了如下的额外要求:

性质1. 节点是红色或黑色。
性质2. 根是黑色。
性质3. 所有叶子都是黑色(叶子是NIL节点)。
性质4. 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)
性质5. 从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点

这些约束强制了红黑树的关键性质:从根到叶子的最长的可能路径不多于最短的可能路径的两倍长。结果是这个树大致上是平衡的。
因为根据属性5所有最长的路径都有相同数目的黑色节点,这就表明了没有路径能多于任何其他路径的两倍长。

红黑树操作:
恢复红黑树的属性需要少量(O(log n))的颜色变更(实际是非常快速的)和不超过三次树旋转(对于插入操作是两次)。
虽然插入和删除很复杂,但操作时间仍可以保持为O(log n)次。

插入节点:
在讨论红黑树的插入操作之前必须要明白,任何一个即将插入的新结点的初始颜色都为红色。这一点很容易理解,因为插入黑点会增加某条路径上黑结点的数目,从而导致整棵树黑高度的不平衡。
如果新结点父结点为红色时(如图2所示),将会违返红黑树性质:一条路径上不能出现相邻的两个红色结点。这时就需要通过一系列操作来使红黑树保持平衡。

删除节点:

1、删除操作中真正被删除的必定是只有一个红色孩子或没有孩子的结点。

2、如果真正的删除点是一个红色结点,那么它必定是一个叶子结点。

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)
	{
		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;	// 節點實值
};


由上面的定义可以看出来,每个结点维护了三个指针和两个信息:父结点parent,左子结点left,右子结点right,同时还有一个颜色标记color,再就是结点的值value_field。同时可以求得当以某个结点为根的最小与最大结点。

二、红黑树的迭代器定义

迭代器是STL里面非常重要的一部分,这里就不细说了。我们直接来看看__rb_tree_iterator提供了哪些功能。因为,__rb_tree_iterator继承自__rb_tree_base_iterator

所以我们先来看__rb_tree_base_iterator这个结构。

truct __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;	// 它用来与容器之间产生一个连结关系(make a reference)
	
	// 以下其實可實作於 operator++ 內,因為再無他處會呼叫此函式了。
	void increment()
	{
		if (node->right != 0) {		// 如果有右子節點。狀況(1)
			node = node->right;		// 就向右走
			while (node->left != 0)	// 然後一直往左子樹走到底
				node = node->left;		// 即是解答
		}
		else {					// 沒有右子節點。狀況(2)
			base_ptr y = node->parent;	// 找出父節點
			while (node == y->right) {	// 如果現行節點本身是個右子節點,
				node = y;				// 就一直上溯,直到「不為右子節點」止。
				y = y->parent;
			}
			if (node->right != y)		// 「若此時的右子節點不等於此時的父節點」。
				node = y;				// 狀況(3) 此時的父節點即為解答。
			// 否則此時的node 為解答。狀況(4)
		}						
		// 注意,以上判斷「若此時的右子節點不等於此時的父節點」,是為了應付一種
		// 特殊情況:我們欲尋找根節點的下一節點,而恰巧根節點無右子節點。
		// 當然,以上特殊作法必須配合 RB-tree 根節點與特殊之header 之間的
		// 特殊關係。
	}
	
	// 以下其實可實作於 operator-- 內,因為再無他處會呼叫此函式了。
	void decrement()
	{
		if (node->color == __rb_tree_red &&	// 如果是紅節點,且
			node->parent->parent == node)		// 父節點的父節點等於自己,
			node = node->right;				// 狀況(1) 右子節點即為解答。
		// 以上情況發生於node為header時(亦即 node 為 end() 時)。
		// 注意,header 之右子節點即 mostright,指向整棵樹的 max 節點。
		else if (node->left != 0) {			// 如果有左子節點。狀況(2)
			base_ptr y = node->left;			// 令y指向左子節點
			while (y->right != 0)				// 當y有右子節點時
				y = y->right;					// 一直往右子節點走到底
			node = y;						// 最後即為答案
		}
		else {							// 既非根節點,亦無左子節點。
			base_ptr y = node->parent;			// 狀況(3) 找出父節點
			while (node == y->left) {			// 當現行節點身為左子節點
				node = y;						// 一直交替往上走,直到現行節點
				y = y->parent;					// 不為左子節點
			}
			node = y;						// 此時之父節點即為答案
		}
	}
};


可以看到__rb_tree_base_iterator里面只有两个函数:increment()decrement()

这两个函数也是很好理解:

increment():找到比当前节点大的最小的那个节点。用于__rb_tree_iterator中重载++操作符的调用。

decrement():找到比当前节点小的最大的那个节点。用于__rb_tree_iterator中重载--操作符的调用。

再来看看__rb_tree_iterator提供了哪些功能:

template <class Value, class Ref, class Ptr>
struct __rb_tree_iterator : public __rb_tree_base_iterator
{
	typedef Value value_type;
	typedef Ref reference;
	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 = x; }
	__rb_tree_iterator(const iterator& it) { node = it.node; }
	
	reference operator*() const { return link_type(node)->value_field; }
#ifndef __SGI_STL_NO_ARROW_OPERATOR
	pointer operator->() const { return &(operator*()); }
#endif /* __SGI_STL_NO_ARROW_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;
	}
};

inline bool operator==(const __rb_tree_base_iterator& x,
                       const __rb_tree_base_iterator& y) {
	return x.node == y.node;
	// 兩個迭代器相等,意指其所指的節點相等。
}

inline bool operator!=(const __rb_tree_base_iterator& x,
                       const __rb_tree_base_iterator& y) {
	return x.node != y.node;
	// 兩個迭代器不等,意指其所指的節點不等。
}

上述代码等价于下面代码,结构功能一目了然:

struct __rb_tree_iterator
{
	typedef __rb_tree_node<T>* link_type;	

	operator*();
	operator->();	
	operator++();	
	operator++(int);
	operator--();	
	operator--(int);
}


三、RB_tree的数据结构:

下面是rb_tree的定义,你可以看到里面有专属的空间配置器,因为红黑树是动态增长的,所以每次增加或删除结点时都要进行配置,它有一个专用的空间配置器,用来每次申请一个结点的内存或归还一个结点的内存。还有各种类别定义,用来维护整棵RB_tree的三笔数据(其中有个仿函数,functor,用来变现节点的大小比较方式),以及一些member functions的定义或声明。

1、专属的空间配置器rb_tree_node_allocator,每次配置一个节点,它使用的是simple_alloc<>。

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;
...
};


2、一些与节点相关的函数get_node(),put_node(),create_node(),clone_node(),destroy_node()。

public:
  // 注意,沒有定義 iterator(喔,不,定義在後面)
  typedef Key key_type;
  typedef Value value_type;
  typedef value_type* pointer;
  typedef const value_type* const_pointer;
  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;
protected:
  link_type get_node() { return rb_tree_node_allocator::allocate(); }
  void put_node(link_type p) { rb_tree_node_allocator::deallocate(p); }

  link_type create_node(const value_type& x) {
    link_type tmp = get_node();			// 配置空間
    __STL_TRY {
      construct(&tmp->value_field, x);	// 建構內容
    }
    __STL_UNWIND(put_node(tmp));
    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);					// 釋還記憶體
  }


3、rb_tree的构造函数

1、以现有的RB_tree复制一个新的RB_tree

2、创造一个空的RB_tree

void init() {
    header = get_node();	// 產生一個節點空間,令 header 指向它
    color(header) = __rb_tree_red; // 令 header 為紅色,用來區分 header  
                                   // 和 root(在 iterator.operator++ 中)
    root() = 0;
    leftmost() = header;	// 令 header 的左子節點為自己。
    rightmost() = header;	// 令 header 的右子節點為自己。
  }
public:
                                // allocation/deallocation
  rb_tree(const Compare& comp = Compare())
    : node_count(0), key_compare(comp) { init(); }

  // 以另一個 rb_tree 物件 x 為初值
  rb_tree(const rb_tree<Key, Value, KeyOfValue, Compare, Alloc>& x) 
    : node_count(0), key_compare(x.key_compare)
  { 
    header = get_node();	// 產生一個節點空間,令 header 指向它
    color(header) = __rb_tree_red;	// 令 header 為紅色
    if (x.root() == 0) {	//  如果 x 是個空白樹
      root() = 0;
      leftmost() = header; 	// 令 header 的左子節點為自己。
      rightmost() = header; // 令 header 的右子節點為自己。
    }
    else {	//  x 不是一個空白樹
      __STL_TRY {
        root() = __copy(x.root(), header);		// ??? 
      }
      __STL_UNWIND(put_node(header));
      leftmost() = minimum(root());	// 令 header 的左子節點為最小節點
      rightmost() = maximum(root());	// 令 header 的右子節點為最大節點
    }
    node_count = x.node_count;
  }
  ~rb_tree() {
    clear();
    put_node(header);
  }
  rb_tree<Key, Value, KeyOfValue, Compare, Alloc>& 
  operator=(const rb_tree<Key, Value, KeyOfValue, Compare, Alloc>& x);


4、rb_tree的元素操作

1)插入元素的插入操作 insert_equal(),插入的值可以是重复的。

// 安插新值;節點鍵值允許重複。
// 注意,傳回值是一個 RB-tree 迭代器,指向新增節點
template <class Key, class Value, class KeyOfValue, class Compare, class Alloc>
typename rb_tree<Key, Value, KeyOfValue, Compare, Alloc>::iterator
rb_tree<Key, Value, KeyOfValue, Compare, Alloc>::insert_equal(const Value& v)
{
  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);
}



2)插入元素的插入操作 insert_unique(),插入的值不能重复的。

// 安插新值;節點鍵值不允許重複,若重複則安插無效。
// 注意,傳回值是個pair,第一元素是個 RB-tree 迭代器,指向新增節點,
// 第二元素表示安插成功與否。
template <class Key, class Value, class KeyOfValue, class Compare, class Alloc>
pair<typename rb_tree<Key, Value, KeyOfValue, Compare, Alloc>::iterator, bool>
rb_tree<Key, Value, KeyOfValue, Compare, Alloc>::insert_unique(const Value& v)
{
  link_type y = header;
  link_type x = root();	// 從根節點開始
  bool comp = true;
  while (x != 0) { 		// 從根節點開始,往下尋找適當的安插點
    y = x;
    comp = key_compare(KeyOfValue()(v), key(x)); // v 鍵值小於目前節點之鍵值?
    x = comp ? left(x) : right(x);	// 遇「大」則往左,遇「小於或等於」則往右
  }
  // 離開 while 迴圈之後,y 所指即安插點之父節點(此時的它必為葉節點)

  iterator j = iterator(y);   // 令迭代器j指向安插點之父節點 y
  if (comp)	// 如果離開 while 迴圈時 comp 為真(表示遇「大」,將安插於左側)
    if (j == begin())   // 如果安插點之父節點為最左節點
      return pair<iterator,bool>(__insert(x, y, v), true);
      // 以上,x 為安插點,y 為安插點之父節點,v 為新值。
    else	// 否則(安插點之父節點不為最左節點)
      --j;	// 調整 j,回頭準備測試...
  if (key_compare(key(j.node), KeyOfValue()(v)))	
    // 小於新值(表示遇「小」,將安插於右側)
    return pair<iterator,bool>(__insert(x, y, v), true);

  // 進行至此,表示新值一定與樹中鍵值重複,那麼就不該插入新值。
  return pair<iterator,bool>(j, false);
}



3)真正的插入操作执行程序 __insert()

template <class Key, class Value, class KeyOfValue, class Compare, class 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) {
// 參數x_ 為新值安插點,參數y_ 為安插點之父節點,參數v 為新值。
  link_type x = (link_type) x_;
  link_type y = (link_type) y_;
  link_type z;

  // key_compare 是鍵值大小比較準則。應該會是個 function object。
  if (y == header || x != 0 || key_compare(KeyOfValue()(v), key(y))) {
    z = create_node(v);  // 產生一個新節點
    left(y) = z;          // 這使得當 y 即為 header時,leftmost() = z
    if (y == header) {
      root() = z;
      rightmost() = z;
    }
    else if (y == leftmost())	// 如果y為最左節點
      leftmost() = z;           	// 維護leftmost(),使它永遠指向最左節點
  }
  else {
    z = create_node(v);		// 產生一個新節點
    right(y) = z;				// 令新節點成為安插點之父節點 y 的右子節點
    if (y == rightmost())
      rightmost() = z;          	// 維護rightmost(),使它永遠指向最右節點
  }
  parent(z) = y;		// 設定新節點的父節點
  left(z) = 0;		// 設定新節點的左子節點
  right(z) = 0; 		// 設定新節點的右子節點
                          // 新節點的顏色將在 __rb_tree_rebalance() 設定(並調整)
  __rb_tree_rebalance(z, header->parent);	// 參數一為新增節點,參數二為 root
  ++node_count;		// 節點數累加
  return iterator(z);	// 傳回一個迭代器,指向新增節點
}


4)调整RB_tree (旋转及改变颜色)

任何插入操作,于节点插入完毕后,都要做一次调整操作,将树的状态调整到符合RB_tree的要求。

__rb_tree_rebalance()是具备如此能力的一个全局函数:

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;	// 令y 為伯父節點
      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->left; // 令y 為伯父節點
      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->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); // 第一參數為左旋點
      }
    }
  }	// while 結束
  root->color = __rb_tree_black;	// 根節點永遠為黑
}

上述的调整函数需要左旋右旋


__rb_tree_rotate_left():左旋

// 以下都是全域函式:__rb_tree_rotate_left(), __rb_tree_rotate_right(),
// __rb_tree_rebalance(), __rb_tree_rebalance_for_erase()

// 新節點必為紅節點。如果安插處之父節點亦為紅節點,就違反紅黑樹規則,此時必須
// 做樹形旋轉(及顏色改變,在程式它處)。
inline void 
__rb_tree_rotate_left(__rb_tree_node_base* x, __rb_tree_node_base*& root)
{
  // x 為旋轉點
  __rb_tree_node_base* y = x->right;	// 令y 為旋轉點的右子節點
  x->right = y->left;
  if (y->left !=0)
    y->left->parent = x;		// 別忘了回馬槍設定父節點
  y->parent = x->parent;

  // 令 y 完全頂替 x 的地位(必須將 x 對其父節點的關係完全接收過來)
  if (x == root)					// x 為根節點
    root = y;
  else if (x == x->parent->left)	// x 為其父節點的左子節點
    x->parent->left = y;
  else							// x 為其父節點的右子節點
    x->parent->right = y;			
  y->left = x;
  x->parent = y;
}


__rb_tree_rotate_right():右旋

// 新節點必為紅節點。如果安插處之父節點亦為紅節點,就違反紅黑樹規則,此時必須
// 做樹形旋轉(及顏色改變,在程式它處)。
inline void 
__rb_tree_rotate_right(__rb_tree_node_base* x, __rb_tree_node_base*& root)
{
  // x 為旋轉點
  __rb_tree_node_base* y = x->left;	// y 為旋轉點的左子節點
  x->left = y->right;
  if (y->right != 0)
    y->right->parent = x; 	// 別忘了回馬槍設定父節點
  y->parent = x->parent;

  // 令 y 完全頂替 x 的地位(必須將 x 對其父節點的關係完全接收過來)
  if (x == root)					// x 為根節點
    root = y;
  else if (x == x->parent->right)	// x 為其父節點的右子節點
    x->parent->right = y;
  else							// x 為其父節點的左子節點
    x->parent->left = y;
  y->right = x;
  x->parent = y;
}


---------------------by----zerocool-----------2012年10月14日3:43:06

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics