为什么STL函数使用节点的颜色来计算std :: map节点的前身

时间:2019-11-15 07:55:46

标签: c++ gcc stl libstdc++ red-black-tree

我正在检查libstdc ++的std::map的实现,并注意到迭代器的递增和递减函数不是完全对称的。 local_Rb_tree_decrement函数(又名前身)具有一个附加子句,用于检查节点的颜色:

static _Rb_tree_node_base*
local_Rb_tree_decrement(_Rb_tree_node_base* __x) throw ()
{
  if (__x->_M_color == _S_red
      && __x->_M_parent->_M_parent == __x)
    __x = __x->_M_right;
  else if (__x->_M_left != 0)
    {
      _Rb_tree_node_base* __y = __x->_M_left;
      while (__y->_M_right != 0)
        __y = __y->_M_right;
      __x = __y;
    }
  else
    {
      _Rb_tree_node_base* __y = __x->_M_parent;
      while (__x == __y->_M_left)
        {
          __x = __y;
          __y = __y->_M_parent;
        }
      __x = __y;
    }
  return __x;
}

第一种情况的目的是什么?为什么节点的颜色会以任何方式影响树的遍历?为何与local_Rb_tree_increment不同?

if (__x->_M_color == _S_red
    && __x->_M_parent->_M_parent == __x)
  __x = __x->_M_right;

提前感谢您的评论和解释!

2 个答案:

答案 0 :(得分:1)

这肯定可以解决end()的情况-请注意无意义的祖父母祖父母的检查和错误的运动方向。而且,毕竟,您不能递增 end()。颜色检查大概是一种优化,可以(有时)避免获取比所需更多的内存。

答案 1 :(得分:1)

经过一番调查,我相信我已经解决了。

标题

libstdc++的{​​{1}}实现在树的顶部有一个附加节点。它称为 Header ,它的 left 子项指向整棵树的最左叶,其 right 子项指向树的最右叶。整个树,而它的 parent 实际上是 Root节点。因此,它有助于获得对树中最小和最大键的恒定时间访问时间(更快的std::mapbegin()操作)。 Root节点 parent 也指向 Header节点,并使用这两个节点创建 parent 循环。 / p>

结束迭代器

Davis Herring已回答,它也用于处理迭代器的rbegin()情况。 end()迭代器的最幼稚的实现将只包含一个end指针。如果到了下一个节点为null的地步,那就是null。它完美无瑕...直到您需要返回。 end的迭代器是双向,您必须能够减少std::map迭代器并获取树的最右边元素。

标题结尾

在这里 Header节点再次提供帮助。在顺序遍历中搜索下一个节点时,我们最终到达了根节点的父节点。我们不是从 right (不是从 left )进入 Header节点的情况,而是将其作为接班人。这样,将 Header 作为end迭代器节点是很自然的。这就是为什么以前的计算条件会检查祖辈等于其自身的节点的原因。这样我们可以检查自己是否在 Header 节点中。这也解释了我们为什么选择正确的子元素: Header 的正确子元素是树的最右节点,这是 inorder遍历中end的前身< / em>。

等等...但是颜色怎么办

是的,上一部分有一个重大错误。有两个节点,它们的祖父母节点等于该节点: Header Root 。从这个角度看,它们是无法区分的。我们需要为 Header节点添加一个单独的标志。

但是,红黑树中的每个节点都已经具有布尔标志: color 。而且,我们还有一个非常好的红黑树属性: Root 始终为 black

将它们放在一起,我们可以完全省略其他标志,并为 Header red 着色。这将是具有祖父母循环的两个节点的区别属性。