C++:二叉搜索树 end() 迭代器
C++: Binary search tree end() iterator
我有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
排序基于插入的方式 - 索引/下标排序。对于map
和set
,它基于密钥排序。对于multimap
和multiset
来说,两者兼而有之。对于unordered_map
等,这种说法非常脆弱,但我仍然可以争论哈希算法和冲突处理。
在逻辑排序中,可以引用有序元素,但有时引用每个元素之间的边界是有意义的。从逻辑上讲(在某些情况下甚至对于实现),这工作相当方便......
| | | | | | | | |
| +-+ | +-+ | +-+ | +-+ | +-+ | +-+ | +-+ | +-+ |
| |0| | |1| | |2| | |3| | |4| | |5| | |6| | |7| |
| +-+ | +-+ | +-+ | +-+ | +-+ | +-+ | +-+ | +-+ |
| | | | | | | | |
0 1 2 3 4 5 6 7 8
你决定零"边界"去哪里与零项去哪里无关,但你总是得到一个简单的加/减关系。如果最小边界的编号与最小元素的编号相同,则最后一个边界的编号比最后一个元素多一个。因此,end
作为一个超越最后一个元素。
在二叉树实现中,可以将每个节点视为具有两个边界 - 元素的两侧各一个。在此方案中,除 begin
和 end
之外的每个绑定都出现两次。您可以使用元素 0 的 RHS 或 LHS 或元素 1 来表示绑定 1。所以原则上你可以使用节点指针和一个标志。但是,与其为大多数边界使用两种表示形式,不如尽可能选择最方便的表示形式 - 您不仅引用正确的边界,而且还引用取消引用时要查看的元素的表示形式。这意味着只有在引用end
时才会设置该标志,在这种情况下,您无论如何都不应该支持取消引用。
你真的不需要遵循这个逻辑,尽管我认为它仍然是一个有用的心智模型。您真正需要的只是一个可识别的end
表示。也许该表示包含指向最终指针的指针很有用(作为例如递减该迭代器的起点)。也许在某些情况下,在内部使用伪迭代器将两个等效边界识别为不同的伪迭代器很方便。
类似但略有不同的模型和选择出现在考虑例如多路树中,其中每个节点都包含一个元素数组。
基本上,我认为在心理上识别绑定位置是不同的但与项目位置相关的是有用的,但这种心智模型不应该限制你的实现选择——它可能会激发替代方案,但它只是一个心理模型。
- 来自 std::list 的迭代器 .end() 按预期返回"0xcdcdcdcdcdcdcdcd"但 .begin()
- 反转依赖于 end() 的迭代器
- 我当前实现的双向链表类是否需要重构迭代器 end() 功能?
- 创建一个简单的前向迭代器,该迭代器在循环缓冲区的"end"处自动换行
- 将迭代器值与 std::min 一起固定到 end()
- 如何使用 SFINAE 从 end() 方法返回 (const_) 迭代器
- 字符串迭代器永远不会达到 std::string::end()
- 当(例如)容器大小发生变化时,容器 end() 迭代器如何演变
- 使用什么作为 end() 迭代器C++?
- std::list.end() 不返回"past-the-end"迭代器
- 迭代器end()函数与指针算术无法使用
- 迭代器:如果 '*' 返回元素的引用,为什么删除 *(myList.end()) 会给出 SegFault
- 向量迭代器对于包装器类不可取消引用(没有超出 end() 取消引用)
- 我可以在戒指中实现迭代器end()吗?
- 输入迭代器的示例`end()`实际上表示一个past-end-end
- 我们可以安全地依靠迭代器的v.end()位置
- 泛型"out of bounds"、"past end"迭代器
- C++将迭代器转换为指针(并将 end() 转换为 nullptr)
- 为什么vector::pop_back使迭代器(end()-1)无效
- 为什么迭代器::end()是非静态成员,与字符串::npos不相似