【问题标题】:how is std::set (red/black tree) forward iteration implemented?std::set(红/黑树)前向迭代是如何实现的?
【发布时间】:2018-05-20 23:38:04
【问题描述】:

如果我对平衡 BST 从最小值到最大值进行了中序遍历,我会使用一个 DFS,它维护一个大小为 lg(n) 的堆栈。但是如果我需要找到任意节点的中序后继,这是最坏情况下的 lg(n) 操作。但是如果我想按顺序迭代,id 需要为每个节点重复找到中序后继,产生 O(n*lg(n))。 std::set 是否使用了一些技巧进行中序迭代,还是真的花费 O(n*lg(n)),或者时间成本是否以某种方式摊销?

【问题讨论】:

  • 我认为通常树的每个节点除了左右节点指针之外还包含一个指向父节点的指针。因此,引用特定节点的迭代器可以在树中以任何需要的方式导航,以获得下一个值(或上一个值)。
  • 是的,我也考虑过这一点,但是现在你有了一个指向父节点的指针,一个指向每个子树的指针,现在每个节点还有两个指针要存储在下一个和上一个节点上。每个节点的内存损失增加了 66%。另外,删除和插入以及可能的重新平衡变得复杂但仍然可行。所以是的,好的,这是一个空间权衡,以避免痛苦的迭代,但是,我想知道是否有一些更好的技巧“他们”使用“免费”或线性摊销
  • 您不需要 next 和 prev 指针。使用父指针,计算下一个/上一个是摊销的常数时间。

标签: c++ algorithm c++11 stl


【解决方案1】:

有序迭代没有技巧;您只需要一个 O(1) 机制来查找当前节点的父节点。

按顺序扫描准确地遍历每个父子边两次:一次从父子到子,一次从子到父。由于树中的边数与非根节点的数量相同,因此完整的迭代执行 Θ(n) 转换以迭代 n 个节点,即是每个节点的摊销常数时间。

查找父节点的常用方法是在节点中存储父链接。额外的链接肯定会增加节点的大小(四个指针而不是三个),但成本在合理范围内。

如果不是 C++ 迭代器失效规则,它要求对有序关联容器的元素的迭代器不能因插入或删除其他元素而失效,那么可以维护一个大小为 O(log n) 在迭代器中。这样的迭代器会很庞大,但并非难以管理(因为 log n 在实践中被限制为一个较小的整数),但在任何树修改后它将无法使用。

【讨论】:

  • 由于迭代器失效规则,在指针中维护堆栈可能会出现问题。
  • “父子 2x 遍历每条边整体”expl。是我正在寻找的洞察力。马上。
猜你喜欢
  • 2014-12-20
  • 2014-07-28
  • 1970-01-01
  • 2011-04-15
  • 2023-03-14
  • 2010-12-14
  • 2012-10-03
  • 2012-08-20
相关资源
最近更新 更多