【问题标题】:How iterating over a std::set returns sorted results迭代 std::set 如何返回排序结果
【发布时间】:2016-02-05 05:14:32
【问题描述】:

容器 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&B 树的实现背后的机制是什么?

【问题讨论】:

  • 我认为这可能与stackoverflow.com/questions/2558153/…重复
  • 这不是上述问题的重复,它基本上回答了我在这个问题中已经说过的一个信息——底层数据结构是 R&B 树。

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


【解决方案1】:

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

所以,归根结底,这是一个教科书式的树遍历实现:迭代器持有一个指向“当前”节点的指针,为了到达下一个节点,只要它来自右子节点,它就会在树中向上上升.如果它来自左孩子,它将下降到右孩子的最左边的孩子节点。

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

    • RB 树节点包含指向父节点的指针,因此不需要堆栈——迭代器只是指向节点的指针,递增或递减它只是遍历树。跨度>
    • 所以使用从叶子到根的反向指针进行顺序遍历?
    猜你喜欢
    • 2012-10-03
    • 2017-03-10
    • 1970-01-01
    • 1970-01-01
    • 2021-06-01
    • 2014-12-18
    • 2019-12-29
    • 1970-01-01
    • 2022-08-17
    相关资源
    最近更新 更多