std :: map迭代器是如何工作的?

时间:2012-09-04 08:29:41

标签: c++ map tree iterator

C ++ STL类std :: map使用二叉树实现O(log(n))查找。但是对于树,迭代器如何工作并不是很明显。 ++运算符在树结构中实际意味着什么?虽然“下一个元素”的概念在数组中有明显的实现,但对于我来说,它在树中并不那么明显。如何实现树迭代器?

3 个答案:

答案 0 :(得分:6)

对于inorder遍历(可能也适用于其他人),如果节点中有父指针,则可以进行非递归遍历。应该可以在你的迭代器中存储两个指针:你需要指明你的位置,你可能(我现在不做研究)需要类似“前一个”指针的东西,这样你就可以搞清楚你当前的移动方向(即我是否需要进入左子树,或者我刚从它回来)。

如果我们刚刚进入节点,那么

“上一步”可能 类似“parent”。如果我们从左子树回来,则“离开”,如果我们从右子树回来,则“向右”,如果我们返回的最后一个节点是我们自己的,则“自”。

答案 1 :(得分:2)

考虑地图中所有元素的集合,这些元素不小于当前元素,也不是当前元素。 “下一个元素”是该组元素中的元素,该元素小于该集合中的所有其他元素。

要使用地图,您必须拥有密钥。并且该密钥必须实现“少于”操作。这决定了地图的形成方式,使得查找,添加,删除,递增和递减操作都是有效的。

通常,地图内部使用某种树。

答案 2 :(得分:2)

我想添加我的两分钱作为评论,但是由于我不能,我将不得不添加一个答案。我一直在谷歌上搜索并感到沮丧,因为我发现的所有答案(假设除外)都假定是堆栈或其他大小可变的数据结构。我确实找到了some code。它表明可以在没有堆栈的情况下完成此操作,但我发现很难遵循,因此决定从首要原则出发来解决问题。

首先要注意的是算法是“左贪婪”。因此,当我们从根开始时,我们会立即尽可能地向左走,因为最左边的节点是我们首先需要的那个节点。这意味着我们永远不需要考虑左子树。它已经被迭代了。

迭代的顺序是左子树,节点,右子树。因此,如果我们位于给定的节点上,那么我们知道它的左子树和该节点本身已经被访问过,接下来我们应该访问右子树(如果有的话),并尽可能向左走。

否则,我们必须上树。如果我们要从一个左孩子到其父母,那么父母就在下一个。 (之后,我们将访问它已经覆盖的正确的子树。)

最后一种情况是我们要从一个合适的孩子转到其父母。父母已经拜访过,所以我们必须再次上去。实际上,我们必须继续攀登直到到达树的根或树,或者发现自己从左手的孩子搬到了父母那里。正如我们已经看到的,在这种情况下,父节点是下一个节点。 (根可以用空指针指示,如我的代码中所示,或某些特殊的前哨节点。)

以下代码可以轻松地适用于STL样式的迭代器

// Go as far left from this node as you can.
// i.e. find the minimum node in this subtree
Node* Leftmost(Node* node)
{
    if (node == nullptr)
        return nullptr;
    while (node->left != nullptr)
        node = node->left;
    return node;
}

// Start iterating from a root node
Node* First(Node* root)
{
    return Leftmost(root);
}

// The iteration is current at node.  Return the next node
// in value order.
Node* Next(Node* node)
{
    // Make sure that the caller hasn't failed to stop.
    assert(node != nullptr);

    // If we have a right subtree we must iterate over it,
    // starting at its leftmost (minimal) node.

    if (node->right != nullptr)
        return Leftmost(node->right);
    
    // Otherwise we must go up the tree

    Node* parent = node->parent;
    if (parent == nullptr)
        return nullptr;

    // A node comes immediately after its left subtree

    if (node == parent->left)
        return parent;

    // This must be the right subtree!
    assert(node == parent->right);

    // In which case we need to go up again, looking for a node that is
    // its parent's left child.

    while (parent != nullptr && node != parent->left)
    {
        node = parent;
        parent = node->parent;
    }

    // We should be at a left child!
    assert(parent == nullptr || node == parent->left);

    // And, as we know, a node comes immediately after its left subtree

    return parent;
}