对std::集进行迭代是如何返回排序结果的

How iterating over a std::set returns sorted results

本文关键字:返回 排序 结果 何返回 std 迭代      更新时间:2023-10-16

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

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

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

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

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

元素按排序顺序返回。考虑到底层数据结构是一棵红黑树,这怎么可能呢?是否存储了一个单独的链表,以便能够从最左边的子树迭代到最右边的子树?如果不是,使用R&B树?

我们可以通过查看源代码(本例中为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;
 }

因此,最终,它是树遍历的教科书式实现:迭代器持有一个指向"当前"节点的指针,为了到达下一个节点,只要它来自正确的子节点,它就会在树中向上移动。如果它来自左子节点,它将下降到右子节点的最左边的子节点。

迭代器执行〔按顺序深度优先的树遍历〕。1这通常在递归算法中实现。由于迭代器的使用不能递归实现,因此迭代器在内部保留一个它所在位置的堆栈,以便它可以返回树。

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