如何迭代std :: set会返回排序结果

时间:2015-11-04 16:02:38

标签: c++ c++11 stl iteration

容器std :: set(或std :: map)是STL提供的数据结构。在几乎所有编译器中,它被实现为R& B树,具有保证的log(n)插入,查找和移除时间。

https://en.wikipedia.org/wiki/Red%E2%80%93black_tree

在红色和黑色树中,元素基于存储元素的“less”运算符进行排序。所以基本上如果一个根是N + 1,N将在左子树上,而N + 2将在右子树上,这个顺序将由less运算符决定。

我的问题是在执行以下代码时:

 set<unsigned long>::iterator it;
 for (it = myset.begin(); it != myset.end(); it++) {
     cout << *it;
 }

元素以排序顺序返回。考虑到底层数据结构是红色和黑色树这一事实,这怎么可能呢?是否存储了单独的链表以便能够从最左边的子树迭代到最右边的子树?如果不是这种使用R&amp; B树的实现背后的机制是什么?

2 个答案:

答案 0 :(得分:8)

我们可以通过查看源代码(本例中为libstdc ++ 5.2.1)找到明确的答案。这就是树节点的样子:

// <libstdc++>/include/bits/stl_tree.h
struct _Rb_tree_node_base {
  typedef _Rb_tree_node_base* _Base_ptr;

  _Rb_tree_color    _M_color;
  _Base_ptr     _M_parent;
  _Base_ptr     _M_left;
  _Base_ptr     _M_right;

  // ...
}

因此每个节点都包含一个颜色,以及指向其父节点及其左右子节点的指针。增量实现为:

//  <libstdc++>/include/bits/stl_tree.h
struct _Rb_tree_iterator {
  _Self& operator++() {
    _M_node = _Rb_tree_increment(_M_node);
    return *this;
  }

// ...

private:
    _Base_ptr _M_node;
};

实际增量不再出现在公共头文件中,而是在库的编译部分:

// <libstdc++>/src/c++98/tree.cc
static _Rb_tree_node_base* local_Rb_tree_increment(_Rb_tree_node_base* __x) throw ()
{
    if (__x->_M_right != 0) {
        __x = __x->_M_right;
        while (__x->_M_left != 0)
            __x = __x->_M_left;
     } else {
         _Rb_tree_node_base* __y = __x->_M_parent;
         while (__x == __y->_M_right)  {
             __x = __y;
             __y = __y->_M_parent;
         }
         if (__x->_M_right != __y)
             __x = __y;
     }
     return __x;
 }

因此,最终,它是树遍历的教科书实现:迭代器保存指向&#34;当前&#34;节点,并且为了到达下一个节点,只要它来自正确的孩子,它就会在树中上升。如果它来自左边的孩子,它将下降到右孩子的最左边的子节点。

答案 1 :(得分:2)

迭代器执行[有序深度优先树遍历]。1这通常在递归算法中实现。由于迭代器的使用不能以递归方式实现,因此迭代器会在内部保留一堆栈,以便它可以返回到树上。

更新:感谢Chris Dodd指出RB树节点有指向父节点的指针,因此迭代器可以简单地跟随那些直到下一个元素。