C++:二进制搜索树end()迭代器

C++:二进制搜索树end()迭代器,c++,iterator,binary-search-tree,ranged-loops,C++,Iterator,Binary Search Tree,Ranged Loops,我没有基本的BST的随机化、排序等实现。我想添加迭代器实现,使BST适合基于范围的for循环。 所以我需要开始,结束成员函数和迭代器递增 我理解begin应该做什么-将迭代器返回到最左下角的节点,并讨论了遍历BST=递增迭代器的不同可能性 但是结尾应该是将迭代器赋给超过最后一个元素的迭代器。这是一个实际的问题,我不明白,在BST的上下文中,这意味着什么?结束迭代器不一定必须是最后一个对向量有意义的元素的过去一个,但对树来说就更少了,例如。。它必须是一个迭代器,可以清楚地标识为不是用于指示到达数据

我没有基本的BST的随机化、排序等实现。我想添加迭代器实现,使BST适合基于范围的for循环。 所以我需要开始,结束成员函数和迭代器递增

我理解begin应该做什么-将迭代器返回到最左下角的节点,并讨论了遍历BST=递增迭代器的不同可能性


但是结尾应该是将迭代器赋给超过最后一个元素的迭代器。这是一个实际的问题,我不明白,在BST的上下文中,这意味着什么?

结束迭代器不一定必须是最后一个对向量有意义的元素的过去一个,但对树来说就更少了,例如。。它必须是一个迭代器,可以清楚地标识为不是用于指示到达数据结构末尾的有效迭代器


实际上,这可以通过几种方式实现,具体取决于迭代器引用它所指向的内容的方式。如果它使用指向树节点的指针,例如,那么可以使用空指针作为结束迭代器。

尽管有我的评论,Sander De Dycker的想法是正确的。我有另一种想法

所有支持迭代器的容器都具有逻辑顺序。对于向量,排序基于插入的方式-索引/下标排序。对于map和set,它基于键顺序。对于multimap和multiset,两者都有一点。对于无序的_映射等,这种说法是非常脆弱的,但我仍然可以争论散列算法和冲突处理

在逻辑排序中,可以引用有序元素,但有时引用每个元素之间的边界是有意义的。从逻辑上讲,在某些情况下,甚至对于实现来说,这也相当方便

|     |     |     |     |     |     |     |     |
| +-+ | +-+ | +-+ | +-+ | +-+ | +-+ | +-+ | +-+ |
| |0| | |1| | |2| | |3| | |4| | |5| | |6| | |7| |
| +-+ | +-+ | +-+ | +-+ | +-+ | +-+ | +-+ | +-+ |
|     |     |     |     |     |     |     |     |
0     1     2     3     4     5     6     7     8
您可以独立于零项的位置来决定零界限的位置,但您总是得到一个简单的加减关系。如果最小边界的编号与最小元素相同,则最后一个边界的编号比最后一个元素多一个。因此,结束作为一个过去的最后一个元素

在二叉树实现中,可以认为每个节点都有两个边界-元素的任意一侧。在该方案中,除开始和结束外,每个边界都出现两次。可以使用元素0的RHS或LHS或元素1表示边界1。因此,原则上可以使用节点指针和标志。不过,您可能会尽可能选择最方便的一种表示方式,而不是对大多数边界使用两种表示方式,即不仅引用右边界,还引用在取消引用时希望看到的元素。这意味着只有在引用end时才会设置该标志,在这种情况下,无论如何都不应该支持取消引用

下面这个逻辑告诉你,你其实不需要遵循这个逻辑,尽管我认为它仍然是一个有用的心智模型。您真正需要的是一个可识别的end表示。对于该表示法来说,将指向最终指针的指针作为起点(例如,递减该迭代器)可能是有用的。也许在某些情况下,可以方便地在内部使用伪迭代器来识别两个不同的等价边界

类似但略有不同的模型和选择会出现,例如,每个节点包含一个元素数组的多路树


基本上,我认为在心理上识别绑定位置是有用的,因为绑定位置是不同的,但与项目位置相关,但是,心智模型不应该约束您的实现选择——它可能会激发其他选择,但它只是一个心智模型。

一个非常简单的方案,使用两个额外的指针值内存,就是简单地在BST顶部覆盖一个双链接的循环列表。您的末端迭代器然后简单地指向一个哨兵节点。它还使迭代器的增量/减量非常简单

BST::iterator &
BST::iterator::operator++() {
  n = n->next;
  return *this;
}

请注意,使用这样的sentinel意味着结束迭代器不需要特殊处理。您可以减少它并获得正确的行为。

std::map如何实现结束迭代器?也许你应该模仿这种行为。@PaulMcKenzie-谁是std::map的实现者?我不知道不同的实现使用不同的设计,但我很确定标准没有规定特定的实现,甚至没有规定使用红黑树IIRC,如果做出不同的选择,我也不会感到惊讶。当然,可能有很好的理由,但标准的图书馆资源也可能不那么容易阅读。不是对风格的批评,而是标准库实现必须处理很多复杂的问题。在某种程度上是正确的,但我认为您对实现的考虑太多了,而不是抽象。排序与地址无关。接口的约定将迭代器重新排序
彼此相对的,包括结束。例如,如果迭代器类型至少是双向的,则递减结束迭代器必须为最终元素提供一个迭代器。当然,这个名称反映了这个意图——它是容器的结束边界。当然,对于无序的_-map等来说,这更尴尬,因为它们没有逻辑顺序,只有散列和冲突处理所定义的顺序。@Steve314:没错,我确实把这个问题解释为关于实现的问题。也许这种解释是错误的。在任何情况下,递减迭代器只需要对末端迭代器进行特殊处理,以使其正常工作。这种方法在多路树中更为常见-两个指针的开销是每个节点的,这可能相当大。二叉树的一种常见方法是使用,在没有子指针的情况下,使用子指针作为顺序后继/前置指针。仍然有一些开销来指示它是哪种链接-child还是succ/pred-但是对于平衡二叉树red-black、AVL等,一个字节可以处理所有的工作。当然,这不是那么简单,而且在线程二叉树中没有明显的哨兵位置,但是,对于二叉树来说,指针指向顺序的第一个和最后一个节点已经足够普遍了,这样就避免了对那些频繁引用的项进行Olog n搜索的需要。