STL源码剖析(十三)关联式容器之rb_tree

STL源码剖析(十三)关联式容器之rb_tree


rt_tree是红黑树,在STL中,它并没有对使用者开放,但它非常重要,它作为set和map的底部支持

这篇文章不详细讨论红黑树的算法,着重讲解STL中红黑树的数据结构,如果需要了解红黑树,可以阅读 数据结构与算法之树(四)红黑树

一、rb_tree的数据结构

template <class Key, class Value, class KeyOfValue, class Compare,
          class Alloc = alloc>
class rb_tree {
  ...
proteced:
  typedef __rb_tree_node<Value> rb_tree_node;

  ...
public:
  typedef rb_tree_node* link_type;
  ...
protected:
  size_type node_count; // keeps track of size of tree
  link_type header;
  Compare key_compare;
  ...
};

rb_tree 中有三个成员变量,node_count 表示红黑树中的节点个数,header 表示头节点(这是STL红黑树的一个特性),key_compare 是一个仿函数,用于比较键值的大小,别忘了红黑树是一种二叉搜索树,是有序的,所以需要有一个比较节点的方法

rb_tree 的节点定义如下

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

template <class Value>
struct __rb_tree_node : public __rb_tree_node_base
{
  typedef __rb_tree_node<Value>* link_type;
  Value value_field;
};

每个节点有颜色 color,有父节点 parent,有左孩子 left,有右孩子 right,还有对应的值 vlur_field

rb_tree的红黑树是经过改造的,跟普通的红黑树还是有点区别,主要是多了 header节点,需要注意的是 header 不是根节点,是一个头节点,如下图所示

在这里插入图片描述

header 节点和根节点互为父节点,header 节点的左子节点指向红黑树最左边的节点,header 节点的右子节点指向红黑树最右边的节点

二、rb_tree的迭代器

struct __rb_tree_base_iterator
{
    
}

template <class Value, class Ref, class Ptr>
struct __rb_tree_iterator : public __rb_tree_base_iterator
{

}

__rb_tree_iterator 继承于 __rb_tree_base_iterator,先查看__rb_tree_base_iterator,其定义如下

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();
  void decrement();
}

其中包含一个成员变量 node,其对应的类型为 typedef __rb_tree_node_base* base_ptr;

node 指向红黑树中的一个节点,其中的 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;
  }
}

decrement 是返回上一个节点,其代码如下

void decrement()
{
  if (node->color == __rb_tree_red &&
      node->parent->parent == node)
    node = node->right;
  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;
  }
}

下面再来看看 __rb_tree_iterator 的定义

template <class Value, class Ref, class Ptr>
struct __rb_tree_iterator : public __rb_tree_base_iterator
{
  /* STL迭代器的设计规范 */
  typedef Value value_type;
  typedef Ref reference;
  typedef Ptr pointer;
  typedef __rb_tree_iterator<Value, Value&, Value*>             iterator;

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

  self& operator++() { increment(); return *this; }
  self& operator--() { decrement(); return *this; }
};

它并没有增加过多的内容,只是重载的自增和自减还有取值操作符,而它的实现也是基于基类的 increment 和 decrement 操作

三、rb_tree的操作

3.1 构造函数

默认构造

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

首先初始化节点数,然后初始化键值比较的仿函数,然后调用 init 初始化 header 节点

下面是 init 的定义

void init() {
  header = get_node(); //分配内存
  color(header) = __rb_tree_red; //将节点设置为红色
  
  root() = 0; //将header的parent设置为NULL

  /* 将 header 的左右孩子设置为本身 */
  leftmost() = header;
  rightmost() = header;
}

其中的 root 函数定义如下

link_type& root() const { return (link_type&) header->parent; }

3.2 析构函数

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

首先调用 clear 清除所有的节点,然后调用 put_node 释放掉 header 节点的内存

clear 的定义如下

void clear() {
   if (node_count != 0) {
     __erase(root()); //销毁整棵树
       
     /* 初始化 header 节点 */
     leftmost() = header;
     root() = 0;
     rightmost() = header;
     node_count = 0;
}

首先通过 __erase 销毁整棵树,然后再是初始化 header 节点

__erase 传递根节点,通过递归的方式来清除整颗树

template <class Key, class Value, class KeyOfValue, class Compare, class Alloc>
void rb_tree<Key, Value, KeyOfValue, Compare, Alloc>::__erase(link_type x) {
                                // erase without rebalancing
  while (x != 0) {
    __erase(right(x)); //递归到最右节点
    link_type y = left(x); //获取左子树
    destroy_node(x); //清除当前节点
    x = y; //跳到左子树继续递归
  }
}

3.3 插入元素

红黑树的插入方法支持 insert,不支持 push,因为红黑树是有序的,插入的节点会自动排序

insert 有两种方法,一种是 insert_equal 支持键值重复,一种是 insert_unique ,键值必须独一无二

红黑树的插入操作较为复杂,这里就不展开讨论了,只需要记住插入的接口就行

 iterator insert_unique(iterator position, const value_type& x);
iterator insert_equal(iterator position, const value_type& x);

3.4 删除元素

删除的操作是 erase,其接口如下

void erase(iterator position); //指定位置
size_type erase(const key_type& x); //指定键值

3.5 其他操作

begin

返回红黑树指向第一个元素的迭代器

link_type& leftmost() const { return (link_type&) header->left; }

iterator begin() { return leftmost(); } //红黑树的最左边节点

end

返回红黑树指向结尾节点的迭代器

iterator end() { return header; } //返回头节点

find

查找算法,STL的算法中页有find函数,但是红黑树自己定义的find函数。使用STL全局的find算法也可以工作,但是由于红黑树自身的特点,提供的一个find成员函数,大大提高查找效率

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>::find(const Key& k) {
  link_type y = header;        // Last node which is not less than k. 
  link_type x = root();        // Current node. 

  while (x != 0) 
    if (!key_compare(key(x), k))
      y = x, x = left(x);
    else
      x = right(x);

  iterator j = iterator(y);   
  return (j == end() || key_compare(k, key(j.node))) ? end() : j;
}

红黑树是一种二叉搜索树,所以查找的方法也就是二分法

**lower_bound **

找到第一个大于等于给定值的节点

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>::lower_bound(const Key& k) {
  link_type y = header; /* Last node which is not less than k. */
  link_type x = root(); /* Current node. */

  while (x != 0) 
    if (!key_compare(key(x), k))
      y = x, x = left(x);
    else
      x = right(x);

  return iterator(y);
}

**upper_bound **

找到第一个大于给定值的节点

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>::upper_bound(const Key& k) {
  link_type y = header; /* Last node which is greater than k. */
  link_type x = root(); /* Current node. */

   while (x != 0) 
     if (key_compare(k, key(x)))
       y = x, x = left(x);
     else
       x = right(x);

   return iterator(y);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值