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

C++: Binary search tree end() iterator

本文关键字:迭代器 end 搜索树 C++      更新时间:2023-10-16

我有BST的基本(无随机化,排序等)实现。我想添加迭代器实现并使 BST 适合基于范围的 for 循环。所以我需要begin(),end()成员函数和迭代器递增。

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

但是 end() 应该为迭代器提供最后一个元素。这是实际的问题,我不明白,在 BST 的背景下,这是什么意思?

结束迭代器不一定是最后一个元素(这对于向量有意义,但对于树则不那么有意义,例如)。它必须只是一个迭代器,可以清楚地标识为不是用于指示数据结构已到达末尾的有效迭代器。

实际上,这可以通过多种方式完成,具体取决于迭代器如何引用它所指向的内容。如果它使用指向树节点的指针,例如,则可以将空指针用于结束迭代器。

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

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

等等。 请注意,使用这样的哨兵意味着end迭代器不需要特殊处理。您可以递减它并获得完全正确的行为。

尽管我发表了评论,但Sander De Dycker的想法是正确的。我有另一种思考方式。

支持迭代器的所有容器都具有逻辑顺序。对于vector排序基于插入的方式 - 索引/下标排序。对于mapset,它基于密钥排序。对于multimapmultiset来说,两者兼而有之。对于unordered_map等,这种说法非常脆弱,但我仍然可以争论哈希算法和冲突处理。

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

|     |     |     |     |     |     |     |     |
| +-+ | +-+ | +-+ | +-+ | +-+ | +-+ | +-+ | +-+ |
| |0| | |1| | |2| | |3| | |4| | |5| | |6| | |7| |
| +-+ | +-+ | +-+ | +-+ | +-+ | +-+ | +-+ | +-+ |
|     |     |     |     |     |     |     |     |
0     1     2     3     4     5     6     7     8

决定零"边界"去哪里与零项去哪里无关,但你总是得到一个简单的加/减关系。如果最小边界的编号与最小元素的编号相同,则最后一个边界的编号比最后一个元素多一个。因此,end作为一个超越最后一个元素。

在二叉树实现中,可以将每个节点视为具有两个边界 - 元素的两侧各一个。在此方案中,除 beginend 之外的每个绑定都出现两次。您可以使用元素 0 的 RHS 或 LHS 或元素 1 来表示绑定 1。所以原则上你可以使用节点指针和一个标志。但是,与其为大多数边界使用两种表示形式,不如尽可能选择最方便的表示形式 - 您不仅引用正确的边界,而且还引用取消引用时要查看的元素的表示形式。这意味着只有在引用end时才会设置该标志,在这种情况下,您无论如何都不应该支持取消引用。

IOW遵循这个逻辑告诉你,

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

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

基本上,我认为在心理

上识别绑定位置是不同的但与项目位置相关的是有用的,但这种心智模型不应该限制你的实现选择——它可能会激发替代方案,但它只是一个心理模型。