节点距离树中另一个节点最远的距离

时间:2013-11-10 11:10:37

标签: c++ algorithm tree

如果输入树,我们需要回答类型的查询,

a)给定上述树的节点,该节点距离该节点最远的节点。

b)从树中删除一组特定的边。

我已经尝试了很长时间,但我能提出的最佳解决方案是,

对于类型a调用dfs函数的查询,该函数将返回O(N)中最远的节点,但我需要做得更好。 对于b类型的查询,只需更新树[删除边缘,如果存在]。

所以我上面的解决方案大致为O(K*N),其中K是查询数,N是节点数。

1 个答案:

答案 0 :(得分:1)

由于您的树是一般树,即它没有平衡的概念或甚至没有根,因此对于一次性查询,您可以做的最好的是O(n)。但是,我认为您可以在花费O(n)时间后设置树,然后让每个后续查询占用一定的时间。

想法是找到树的“中间”,将树分成大致相等大小的树,调用部分任意,例如 left right 。然后,用它们所在的部分标记各自部分中的所有节点,并存储距离中间最远的左节点和右节点。当您获得节点的查询时,您只需查看节点的标签并在另一侧报告存储的节点。

鉴于评论[和无根据的downvote],似乎解决方案需要更多解释。首先,给定节点的最远节点通常不是唯一的。想象一下,例如一个正好有三个节点的路径。中间节点有两个最远的节点。其中任何一个都是解决方案。基于此,我们的想法是在树中找到一个节点,该节点位于树中两个最远节点之间的路径中间(这些节点之间的距离是奇数,可以选择任一侧的节点)这样距离只相差一个):如果距离最远的节点 l 节点分开,则中间节点的长度为 l / 2 ,两者都是 l / 2 到一个路径, l / 2 + 1 路径到另一个路径。

使用此中间节点将树分成两半,随机调用 left right 一半,可以确定任何给定的最远的节点节点,如果每个节点知道它是在左侧还是在右半部分:最长的路径将通过中间节点进入另一半并从那里到达距离中间最远的节点。让我们调用左侧 ll 中最长路径的长度和右侧 lr 中最长路径的长度。不失一般性, lr< ll (只需交换周围的名字)。与中间相距最远的节点称为 nl nr 。注意,如果有多个子树从中间节点引出,这些子树被认为是右边部分的一部分,只要最长路径之一(或者如果它是唯一的最长路径)在左边部分。

当您想要从节点 n 中声明距离最远的节点时,需要考虑三种情况:

  1. 节点 n 是中间节点。在这种情况下,最远的节点显然是 nl
  2. 节点 n 位于树的右侧。您可以构建的最长路径行进到中间,然后到 nl ,即最远的节点显然 nl
  3. 节点 n 位于树的左侧。同样,您可以构建的最长路径行进到中间但从那里到 nr
  4. 唯一的问题是如何在O(n)时间内找到中间节点:

    1. 找到所有叶子节点并将它们放入队列中,用1标记它们并给它们一个0的距离。这可以在O(n)时间[和空间]完成。
    2. 从队列中读取(但不要额外)第一个节点并查找所有相邻节点。如果存在标签小于其相邻节点数的节点,请增加标签。如果标签现在与相邻节点的数量匹配,则将节点添加到队列中,并使其比队列中的第一个节点大一个距离。
    3. 如果队列中只有一个节点,则此节点是中间节点,此步骤将终止。
    4. 否则,提取前节点并继续处理队列(即步骤2)。
    5. 作为最后一遍,找到距离标签最大的相邻节点,并考虑左侧树上悬挂此节点的树。将节点标记为具有BFS的左节点时,跟踪队列中的最后一个节点以查找 nl 。考虑所有其他子树右树,并用BFS标记它们作为正确的节点,也找到 nr

      我想,树的预处理可以更优雅地完成,也可能使用少量通道,但我确信上述方法确实有效。