二进制搜索树中按顺序遍历的复杂性(使用迭代器)
In-order traversal complexity in a binary search tree (using iterators)?
相关问题:二叉树O(N)的无序树遍历的时间复杂性?,然而,它是基于通过递归的遍历(因此在O(log N)空间中),而迭代器只允许消耗O(1)空间。
在C++中,通常要求增加标准容器的迭代器是O(1)运算。对于大多数容器,这是微不足道的证明,然而对于map
等容器,这似乎有点困难。
- 如果
map
被实现为跳过列表,那么结果将是显而易见的 - 然而,它们通常被实现为红黑树(或者至少是二进制搜索树)
因此,在按顺序遍历过程中,有时不太容易达到"下一个"值。例如,如果您指向左子树的右下叶,那么下一个要遍历的节点就是根,它离depth
步之遥。
我试着"证明"算法的复杂性(以"步骤"为单位)是摊销的O(1),这似乎没问题。然而,我还没有完成演示。
这是我为一棵深度为4的树绘制的一个小图,数字(在节点的位置)表示在按顺序遍历过程中从该节点到下一个节点的步骤数:
3
2 2
1 1 1 1
1 2 1 3 1 2 1 4
注意:最右边的叶子的成本为4,以防这是一棵大树的子树
对于15个节点的总数,总和为28:因此平均每个节点的成本小于2,这(如果它成立的话)将是一个不错的摊销成本。因此:
- 在按顺序遍历过程中,对于平衡(和完整)的二进制搜索树,递增迭代器真的是O(1)吗
- 可以将结果扩展到非完全二进制搜索树吗
是的,对于任何树,每次迭代的摊余成本实际上是O(1)
。
证据是基于你"访问"每个节点的次数。
树叶只被造访一次。无树叶最多访问3次:
- 从左子树返回时
- 从右侧子树返回时
不再有对任何节点的访问,因此,如果我们对每个节点的访问次数求和,我们得到一个小于3n
的数字,因此所有节点的访问总数加起来就是O(n)
,这就得到了每一步摊销的O(1)
。
(注意,由于在一棵完整的树中有n/2片叶子,我们得到的是你遇到的2n
,我相信可以表明,对于任何树,访问量的总和都会小于2n
,但这种"优化"不在IMO的范围内)。
每个步骤的最坏情况是O(h)
,它是平衡树中的O(logn)
,但在某些情况下可能是O(n)
。
p.S.我不知道红黑树是如何在C++中实现的,但如果树数据结构包含每个节点的parent
字段,它可以替换递归堆栈并允许O(1)
空间消耗。(这当然是"欺骗",因为存储n
这样的字段本身就是O(n)
)。
- 使用std::multimap迭代器创建std::list
- 来自 std::list 的迭代器 .end() 按预期返回"0xcdcdcdcdcdcdcdcd"但 .begin()
- C++中带有List类的迭代器Segfault
- 如何在c++迭代器类型中包装std::chrono
- 集合上的输出迭代器:assign和increment迭代器
- Boost Spirit,获取迭代器内部语义动作
- 对于set上的循环-获取next元素迭代器
- 为什么output_editor Concept不需要output_e迭代器标记
- c++17文件系统::recursive_directory迭代器()在mac上没有给出这样的目录,但在windows上
- 使用迭代器时如何访问对象在向量中的位置?
- std::vector::迭代器是否可以合法地作为指针
- 跟随整数索引列表的自定义类迭代器
- 不明白迭代器,引用和指针失效,一个例子
- 我可以使用反向迭代器作为ForwardIt吗
- ESP8266单片机矢量迭代器的C++问题
- 如何在C++中将迭代器作为函数参数传递
- std::unordered_set迭代器遍历的复杂性
- 二进制搜索树中按顺序遍历的复杂性(使用迭代器)
- map/set :: 插入是否提供了正确的迭代器提示,它的复杂性是多少?
- 为什么c++标准需要std::分区来满足不同类型迭代器的不同复杂性