在这个问题中,我不是问怎么做,而是怎么做的 我正在尝试(作为一个例外)实现简单的地图,虽然我没有实现链接的问题和他们的行为(如何找到下一个地方插入新链接等)我坚持问题如何实现迭代一张地图。当你考虑它并查看std :: map时,这个映射能够返回开始和结束迭代器。怎么样?特别是结束? 如果map是树,你怎么能说这张地图的哪个分支结束?我只是不明白。如何迭代地图?从树的顶部开始然后呢?去并列出左边的所有内容?但是左边的那些节点也有右边的链接。我真的不知道。如果有人可以向我解释或给我一个链接,我会很高兴,所以我可以阅读它。
答案 0 :(得分:3)
使用二叉搜索树实现map
。为了满足复杂性要求,它必须是一个自平衡树,因此通常使用红黑树,但这不会影响您在树上迭代的方式。
要按从最小到最大的顺序从二叉搜索树中读取元素,您需要执行树的有序遍历。递归实现非常简单,但在迭代器中使用并不实际(迭代器必须在内部维护一个堆栈,这会使复制起来相对昂贵)。
您可以实现迭代按顺序遍历。这是我刚才写的树容器库中的一个实现。 NodePointerT
是指向节点的指针,其中节点具有left_
类型的right_
,parent_
和NodePointerT
指针。
// Gets the next node in an in-order traversal of the tree; returns null
// when the in-order traversal has ended
template <typename NodePointerT>
NodePointerT next_inorder_node(NodePointerT n)
{
if (!n) { return n; }
// If the node has a right child, we traverse the link to that child
// then traverse as far to the left as we can:
if (n->right_)
{
n = n->right_;
while (n->left_) { n = n->left_; }
}
// If the node is the left node of its parent, the next node is its
// parent node:
else if (n->parent_ && n == n->parent_->left_)
{
n = n->parent_;
}
// Otherwise, this node is the furthest right in its subtree; we
// traverse up through its parents until we find a parent that was a
// left child of a node. The next node is that node's parent. If
// we have reached the end, this will set node to null:
else
{
while (n->parent_ && n == n->parent_->right_) { n = n->parent_; }
n = n->parent_;
}
return n;
}
要查找begin
迭代器的第一个节点,您需要在树中找到最左边的节点。从根节点开始,按照左子指针,直到遇到没有左子节点的节点:这是第一个节点。
对于end
迭代器,您可以将节点指针设置为指向根节点或树中的最后一个节点,然后在迭代器中保留一个标志,指示它是结束迭代器({{ 1}}或类似的东西)。
答案 1 :(得分:2)
地图迭代器的表示完全取决于您。我认为使用单个包装指针到node
就足够了。 E.g:
template <typename T>
struct mymapiterator
{
typename mymap<T>::node * n;
};
或类似的东西。现在,mymap::begin()
可以返回n
指向最左边节点的迭代器的这种实例。 mymap::end()
可以返回实例,其中n
可能指向root,或者某些其他特殊节点仍然可以返回到最右边的节点,以便它可以满足来自end迭代器的双向迭代。
在节点(operators++()
和operator--()
等)之间移动的操作是关于从较小值到较大值遍历树,反之亦然。您可能已在插入操作实现期间实现的操作。
答案 2 :(得分:1)
出于排序目的,地图的行为类似于排序的键/值容器(a.k.a。字典);您可以将其视为键/值对的有序集合,这正是您在查询迭代器时获得的。观察:
map<string, int> my_map;
my_map["Hello"] = 1;
my_map["world"] = 2;
for (map<string, int>::const_iterator i = my_map.begin(); i != my_map.end(); ++i)
cout << i->first << ": " << i->second << endl;
就像任何其他迭代器类型一样,map迭代器的行为类似于指向集合元素的指针,对于map,这是一个std :: pair,其中first
映射到键和second
映射到值。
std::map
在内部使用二进制搜索,但你不需要直接访问树表示。
答案 3 :(得分:0)
你可能遗漏的一个重要技巧是end()
迭代器不需要指向任何东西。它可以是NULL或任何其他特殊值。
++
运算符在迭代器越过地图末尾时将迭代器设置为相同的特殊值。一切正常。
要实现++
,您可能需要在每个节点中保留next / prev指针,或者您可以通过将刚刚离开的节点与父节点的最右侧节点进行比较来向后移动树以查找下一个节点看你是否需要走到那个父节点等
不要忘记映射的迭代器在插入/擦除操作期间应该保持有效(只要你没有删除迭代器的当前节点)。