以下是一个 O(n log n) 算法,适用于每个孩子最多有一个父级的图(编辑:此算法不会扩展到具有 O(n log n) 性能的双父级情况)。值得注意的是,我相信通过额外的工作可以将性能提高到 O(n log(max level label))。
一位家长案例:
对于每个节点 x,以相反的拓扑顺序,创建一个二叉搜索树 T_x,它的出生日期和从 x 中删除的代数都严格增加。(T_x 包含以 x 为根的祖先图的子图中的第一个出生的孩子 c1,以及该子图中的下一个最早出生的孩子 c2,因此 c2 的“曾祖父母级别”a 严格大于 c1 的级别,以及此子图中的下一个最早出生的孩子 c3,使得 c3 的级别严格大于 c2 的级别等。)为了创建 T_x,我们合并先前构建的树 T_w,其中 w 是 x 的孩子(它们是先前构建的,因为我们以相反的拓扑顺序迭代)。
如果我们小心我们如何执行合并,我们可以证明这种合并的总成本对于整个祖先图是 O(n log n)。关键思想是要注意,每次合并后,每一级最多有一个节点在合并树中存活。我们将每棵树 T_w 与 h(w) log n 的势相关联,其中 h(w) 等于从 w 到叶子的最长路径的长度。
当我们合并子树 T_w 以创建 T_x 时,我们“销毁”所有树 T_w,释放它们存储的所有潜力以用于构建树 T_x;我们创建一个具有 (log n)(h(x)) 潜力的新树 T_x。因此,我们的目标是花费最多 O((log n)(sum_w(h(w)) - h(x) + constant)) 时间从树 T_w 创建 T_x,以便合并的摊销成本为只有 O(log n)。这可以通过选择树 T_w 来实现,使得 h(w) 最大作为 T_x 的起点,然后修改 T_w 以创建 T_x。在为 T_x 做出这样的选择之后,我们使用类似于合并两棵二叉搜索树的标准算法的算法将其他每一棵树一一合并到 T_x 中。
本质上,合并是通过迭代 T_w 中的每个节点 y 来完成的,按出生日期搜索 y 的前任 z,然后如果从 x 中删除的级别多于 z,则将 y 插入到 T_x 中;然后,如果 z 被插入到 T_x 中,我们在 T_x 中搜索严格大于 z 级别的最低级别的节点,并拼接出中间节点以保持 T_x 按出生日期和级别严格排序的不变量。T_w中每个节点的成本为O(log n),而T_w中最多有O(h(w))个节点,所以合并所有树的总成本为O((log n)(sum_w(h(w) ))),对除子 w' 之外的所有子 w 求和,使得 h(w') 最大。
我们将与 T_x 的每个元素关联的级别存储在树中每个节点的辅助字段中。我们需要这个值,以便在构造 T_x 后计算出 x 的实际水平。(作为技术细节,我们实际上将每个节点与其父节点的级别差异存储在 T_x 中,以便我们可以快速增加树中所有节点的值。这是标准的 BST 技巧。)
就是这样。我们只是注意到初始潜力为 0,最终潜力为正,因此摊销边界的总和是整个树中所有合并总成本的上限。一旦我们通过对 T_x 中在 x 以成本 O(log n) 死亡之前出生的最新元素进行二进制搜索来创建 BST T_x,我们就会找到每个节点 x 的标签。
为了提高对 O(n log(max level label)) 的限制,您可以延迟合并树,仅根据需要合并树的前几个元素,以便为当前节点提供解决方案。如果您使用利用局部引用的 BST,例如 splay 树,则可以实现上述界限。
希望上述算法和分析至少足够清晰,可以遵循。如果您需要任何澄清,请发表评论。