关于RB树迭代器的减少函数的疑虑

huangapple go评论66阅读模式
英文:

Doubts about the decrement function of the RB-Tree iterator

问题

我正在编写一个类似STL的容器库js-sdsl,使用Js模式。

关于STL RB-Tree中的减量函数(迭代器的前进函数)有一个问题。

当我将两个不同的值从小到大插入树中时,我将得到一个具有以下结构的RB-Tree:

关于RB树迭代器的减少函数的疑虑

其中,根节点标头节点互为父节点和子节点,标头节点的左右节点分别指向两个插入的值,较小的值将成为根节点,较大的值将成为根节点的右子节点。

此时,当指向根节点的迭代器减少时,将执行以下步骤:

  1. 判断当前是否为标头节点,进入步骤2;
  2. 判断当前指针的左子节点是否为空,进入步骤3;
  3. 确定当前节点(根节点)是父节点(标头节点)的左子节点(注意此时标头节点最左节点指向树中最小的节点,即根节点),因此将指针移动到父节点(标头节点),并重复步骤3,直到不再满足条件为止,进入步骤4;
  4. 显然,由于根节点标头节点互为父节点和子节点,所以在步骤3之后,指针最终指向根节点

最终,以下代码应该陷入无限循环:

set<int> st { 1, 2 };
for (auto it = st.rbegin(); it != st.rend(); ++it) {
    cout << *it << endl;
}

但实际上,上述代码正常运行,我感到困惑,我没有看到STL代码中有任何特殊处理,我不擅长C++,是否有人可以帮助我解决这个问题?

非常感激!

为什么上述代码正常运行

英文:

I'm writing a STL-like container library js-sdsl in Js mode.

There is a question about the decrement function (forward function of the iterator) in STL RB-Tree.

When I insert two different values into the Tree from small to large, I will get an RB-Tree with the following structure:

关于RB树迭代器的减少函数的疑虑

Among them, the Root node and the Header node are parents and children of each other, and the left and right nodes of the Header node point to the two inserted values respectively, and the smaller value will become the Root node, and the larger value will become the right child node of Root.

At this point, when the iterator pointing to the Root node is decremented, the following steps will be taken:

  1. Judging that it is not currently a Header node, go to step 2;
  2. Judging that the left child node of the current pointer is empty, go to step 3;
  3. Determine that the current node (Root) is the left child node of the parent node (Header) (note that at this time, the LeftMost of the Header points to the smallest node in the tree, that is, Root), so move the pointer to the parent node (Header), and repeat step 3 until no more If the conditions are met, go to step 4;
  4. Obviously, because the Root node and the Header node are parents and children of each other, the pointer finally points to the Root node after step 3.

In the end, the following code should fall into an infinite loop:

set&lt;int&gt; st { 1, 2 };
for (auto it = st.rbegin(); it != st.rend(); ++it) {
    cout &lt;&lt; *it &lt;&lt;endl;
}

But actually, the above code works fine, I'm confused, I don't see any special handling in the STL code, I'm not proficient in C++, can anyone help me solve this problem?

Grateful!

Why above code works fine

答案1

得分: 1

我认为你误解了std::reverse_iterator的工作原理:当调用重载的operator*时,底层迭代器会被减少然后解引用。更多详细信息可以在std::reverse_iterator中找到。

以下是对循环的解释:

  1. 在第一次循环中,当前是rbegin()的迭代器被解引用,这意味着底层迭代器被减少然后解引用。由于迭代器指向了哨兵节点(头部),减少函数将其移动到其右子节点,即定义上的最右节点。
  2. 在第二次循环中,更新后的迭代器被解引用,这意味着底层迭代器被减少然后解引用。由于迭代器指向一个外部节点,其左子节点为空,然后减少函数将其移动到其父节点(根节点)。
  3. 最后,由于更新后的迭代器现在等于rend(),即根节点,循环被终止。

实际上,你关于begin()迭代器的减少的观察是正确的:如果它被减少,它再次指向自己。

在我看来,我更喜欢一个更干净的红黑二叉搜索树实现,它以另一种方式组织结构并使用另一种树遍历算法,以便哨兵节点既是最左元素的前一个元素,又是最右元素的下一个元素(循环迭代)。然而,这不会允许对最右节点的常数时间访问,因此可以理解为什么许多库决定使用当前的实现方式 关于RB树迭代器的减少函数的疑虑

英文:

I think you misunderstood how the std::reverse_iterator works: when the overloaded operator* is called, the underlying iterator is decremented and then dereferenced. More details can be found at std::reverse_iterator

The loop should be interpreted in the following way:

  1. At first loop, the iterator, that is currently rbegin(), is dereferenced, that means the underlying iterator is decremented and then dereferenced. Since the iterator points to the sentinel node (Header), the decrement function moves it to its right child node, that is the rightmost node by definition.
  2. At second loop, the updated iterator is dereferenced, that means the underlying iterator is decremented and then dereferenced. Since the iterator points to an external node, its left child node is null and then the decrement function moves it to its parent node (Root).
  3. Finally, since the updated iterator is now equal to the rend(), that is the root node, the loop is broken.

Effectively, your observation about the decrement of the begin() iterator is right: if it is decremented, it refers again to itself.

In my humble opinion, I prefer a more clean implementation of the red-black binary search tree, that organizes the structure in another way and uses another algorithm for tree traversal, so that the sentinel node is both the previous element of the leftmost element and the next element of the rightmost element (cyclic iteration). However, it would not allow constant-time access to the rightmost node, then it is understandable that many libraries decided to use the current implementation 关于RB树迭代器的减少函数的疑虑

huangapple
  • 本文由 发表于 2023年6月16日 15:18:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/76487807.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定