迭代地图
在这个问题中,我不是问如何做,而是问它是如何完成的。
我正在尝试(作为练习)实现简单的地图,尽管我在实现链接和它们的行为(如何找到下一个位置插入新链接等)方面没有问题,但我遇到了如何实现迭代的问题一张地图。当您考虑并查看 std::map 时,该映射能够返回开始和结束迭代器。如何?特别是结尾?
如果地图是一棵树,你怎么能说这个地图的哪一个分支是终点呢?我只是不明白。如何迭代地图?从树顶开始然后呢?去把所有的东西都列在左边吗?但左边的那些节点也有到右边的链接。我真的不知道。如果有人可以向我解释它或给我一个链接以便我可以阅读它,我将非常高兴。
In this question I'm not asking how to do it but HOW IS IT DONE.
I'm trying (as an excersise) implement simple map and although I do not have problems with implementing links and they behavior (how to find next place to insert new link etc.) I'm stuck with the problem how to implement iterating over a map. When you think about it and look at std::map this map is able to return begin and end iterator. How? Especially end?
If map is a tree how can you say which branch of this map is an end? I just do not understand it. An how to iterate over a map? Starting from the top of the tree and then what? Go and list everything on the left? But those nodes on the left have also links to the right. I really don't know. I will be really glad if someone could explain it to me or give me a link so I could read about it.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
map
使用二叉搜索树实现。为了满足复杂性要求,它必须是自平衡树,因此通常使用红黑树,但这不会影响您迭代树的方式。要按从最小到最大的顺序从二叉搜索树中读取元素,您需要对树执行中序遍历。递归实现非常简单,但在迭代器中使用并不实际(迭代器必须在内部维护一个堆栈,这将使其复制成本相对较高)。
您可以实现迭代的有序遍历。这是取自我不久前编写的树容器库的实现。
NodePointerT
是一个指向节点的指针,该节点有left_
、right_
和parent_
类型的指针代码>NodePointerT。要找到
begin
迭代器的第一个节点,您需要找到树中最左边的节点。从根节点开始,沿着左子节点指针前进,直到遇到没有左子节点的节点:这是第一个节点。对于
end
迭代器,您可以将节点指针设置为指向根节点或树中的最后一个节点,然后在迭代器中保留一个标志,指示它是一个结束迭代器(>is_end_
或类似的东西)。A
map
is implemented using a binary search tree. To meet the complexity requirements it has to be a self-balancing tree, so a red-black tree is usually used, but that doesn't affect how you iterate over the tree.To read the elements out of a binary search tree in order from least to greatest, you need to perform an in-order traversal of the tree. The recursive implementation is quite simple but isn't really practical for use in an iterator (the iterator would have to maintain a stack internally, which would make it relatively expensive to copy).
You can implement an iterative in-order traversal. This is an implementation taken from a library of tree containers I wrote a while ago.
NodePointerT
is a pointer to a node, where the node hasleft_
,right_
, andparent_
pointers of typeNodePointerT
.To find the first node for the
begin
iterator, you need to find the leftmost node in the tree. Starting at the root node, follow the left child pointer until you encounter a node that has no left child: this is the first node.For an
end
iterator, you can set the node pointer to point to the root node or to the last node in the tree and then keep a flag in the iterator indicating that it is an end iterator (is_end_
or something like that).地图迭代器的表示完全取决于您。我认为使用指向节点的单个包装指针就足够了。例如:
或者类似的东西。现在,
mymap::begin()
可以返回这样的迭代器实例,n
将指向最左边的节点。mymap::end()
可以返回带有n
的实例,该实例可能指向根节点或其他一些特殊节点,从中仍然可以返回到最右边的节点,以便它可以满足从结束迭代器开始的双向迭代。节点之间移动的操作(
operators++()
和operator--()
等)是从较小的值到较大的值遍历树,反之亦然。您可能在执行插入操作期间已经执行过的操作。The representation of your map's iterator is totally up to you. I think it should suffice to use a single wrapped pointer to a
node
. E.g.:Or something similar. Now,
mymap::begin()
could return such instance of the iterator thatn
would point to the leftmost node.mymap::end()
could return instance withn
pointing to root probably or some other special node from which it is still possible to get back to rightmost node so that it could satisfy bidirectional iteration from end iterator.The operation of moving between the nodes (
operators++()
andoperator--()
, etc.) are about traversing the tree from smaller to bigger values or vice versa. Operation that you probably have already implemented during insertion operation implementation.出于排序目的,映射的行为类似于排序的键/值容器(也称为字典);您可以将其视为键/值对的排序集合,这正是查询迭代器时得到的结果。观察:
就像任何其他迭代器类型一样,映射迭代器的行为类似于指向集合元素的指针,对于映射,这是一个 std::pair,其中
first
映射到键和Second
映射到该值。当您调用其 find() 方法或使用运算符[] 时,std::map 会在内部使用二分搜索,但您不需要直接访问树表示。
For sorting purposes, a map behaves like a sorted key/value container (a.k.a. a dictionary); you can think of it as a sorted collection of key/value pairs, and this is exactly what you get when you query for an iterator. Observe:
Just like any other iterator type, the map iterator behaves like a pointer to a collection element, and for map, this is a std::pair, where
first
maps to the key andsecond
maps to the value.std::map
uses a binary search internally when you call its find() method or use operator[], but you shouldn't ever need to access the tree representation directly.您可能会忽略的一个重要技巧是
end()
迭代器不需要指向任何内容。它可以是 NULL 或任何其他特殊值。当迭代器越过映射末尾时,
++
运算符将迭代器设置为相同的特殊值。然后一切正常。要实现
++
,您可能需要在每个节点中保留下一个/上一个指针,或者您可以通过将刚刚离开的节点与父级最右边的节点进行比较,沿着树向上查找下一个节点节点来查看您是否需要走到那个父节点等。不要忘记,映射的迭代器应该在插入/擦除操作期间保持有效(只要您没有擦除迭代器的当前节点)。
One big trick you may be missing is that the
end()
iterator does not need to point to anything. It can be NULL or any other special value.The
++
operator sets an iterator to the same special value when it goes past the end of the map. Then everything works.To implement
++
you might need to keep next/prev pointers in each node, or you could walk back up the tree to find the next node by comparing the node you just left to the parent's right-most node to see if you need to walk to that parent's node, etc.Don't forget that the iterators to a map should stay valid during insert/erase operations (as long as you didn't erase the iterator's current node).