Algorithm 算法-所有节点在树中的最大距离

Algorithm 算法-所有节点在树中的最大距离,algorithm,search,tree,Algorithm,Search,Tree,所以。。查找树中两个节点之间的最长路径相当容易。但是我想要的是为所有x查找从节点x到树中另一个节点的最长路径 这个问题也可以用以下方式表达:计算从给定的树可以生成的所有有根树的高度 当然,一种方法是,对树中的所有节点执行BFS/DFS,并记住每个节点找到的最远节点。然而,这导致O(N2)。有可能做得更好吗 编辑:简单回顾一下,我的问题是而不是如何在图中找到最长路径。如果可能的话,它是如何为中的所有节点x找到包含给定节点x的最长路径,比O(N2)时间复杂度更好。您的问题归结为在树中找到最长路径。这

所以。。查找树中两个节点之间的最长路径相当容易。但是我想要的是为所有
x
查找从节点
x
到树中另一个节点的最长路径

这个问题也可以用以下方式表达:计算从给定的树可以生成的所有有根树的高度

当然,一种方法是,对树中的所有节点执行BFS/DFS,并记住每个节点找到的最远节点。然而,这导致O(N2)。有可能做得更好吗


编辑:简单回顾一下,我的问题是而不是如何在图中找到最长路径。如果可能的话,它是如何为中的所有节点
x
找到包含给定节点
x
的最长路径,比O(N2)时间复杂度更好。

您的问题归结为在树中找到最长路径。这也称为树的直径

这是一个研究得很好的主题,有很多资源提供了一个O(n)算法,其中n是图中的节点数

请参见和

是的,有一个O(n)算法

把树想象成无根的——只是一个图,其中每个节点都有双向边,不形成循环

对于具有相邻节点的给定节点p,例如a_i,我们将计算高度Hpa_i。高度HPAII是根P的子树的高度(即对于算法的这一部分,我们暂时考虑根子树),通过考虑节点AAI来得到P的父。 如果你感兴趣的是从每个节点到一个叶子的最长路径(你的问题加上它的标题让人怀疑你到底在计算什么),那么它就是max{Hpa_i for all i}。相应的i值给出了最长路径本身

另一方面,如果你对通过p的最长路径感兴趣,那将是从{len(p--a_i)+Ha_ip for all i}中选择的最大对的和,i的两个对应值给出了最长路径本身

因此,如果我们有每个节点的高度,那么得到最终结果就是一个简单的O(n)作业

只剩下计算所有节点的高度。为此,从一个特殊的深度优先搜索开始。它接受2个节点作为参数。第一个节点p是正在搜索的节点,第二个节点q\in{a_i}是当前被视为p的父节点的相邻节点。假设U是一个将节点对带到高度的映射:(p,q)->Hpq

函数搜索和标签(p,q)
if((p,q)映射到U中的高度Hpq){return Hpq}
如果(p==null){将(p,q)->0添加到U并返回0}
设h=max(所有x与p相邻,不等于q){
len(p--x)+搜索_和_标签(x,p)
}
将(p,q)->h添加到U中
返回h
现在我们可以找到所有的高度

为所有节点p和相邻节点x添加映射(p,x)->null到U
还为所有相邻节点p<3的节点添加一个映射(p,z)->null到U
while(U包含形式为(p,x)->null的映射)
search_和_label(p,x)//这将用高度替换空映射
这将是一个O(n)计算,因为它在每条边上花费恒定的工作量,并且树中的边数为n-1

代码

今天下雨了,所以这里有一些代码生成一个随机树,并用O(n)时间内的最长路径信息标记它。首先是典型的输出。每个节点都标有自己的编号,然后是包含它的最长路径的长度,然后是该路径上相邻节点的编号。小边标签是高度信息。首先是相对子树的高度以及该子树中最长叶子路径的节点:

导入java.io.File;
导入java.io.FileNotFoundException;
导入java.io.PrintStream;
导入java.util.HashMap;
导入java.util.Map;
导入java.util.Random;
/**
*无向图。它有一个生成器,用随机变量填充图形
*无根树。它知道如何用最长的路径来装饰自己
*当它是这样一棵树的时候。
*/
类图{
/**
*边p--q表示为边[p][q]=dq和边[q][p]=dp,其中dq和
*dp是节点数据。它们描述了边缘的各个端点:
*
    *
  • dq.len==dp.len,边缘长度 *
  • dq.h是以q为父树,根植于p的子树的高度。 *
  • dq。下一个是p(相对于父q)的子代,p是max的根 *高度子树。 *
*/ 最终映射边=新HashMap(); /** *图中的节点。 */ 静态类节点{ final int id;//唯一的节点id。 节点a,b;//最长路径上的相邻节点。 double len;//最长路径的长度。 节点(int i){ 这个id=i; } } /** *与图形中边的一端关联的数据。 */ 静态类边数据{ 最终双透镜;//边缘长度。 双h;//子树高度。 Node next;//到叶的最大长度路径上的下一个节点。 EdgeData(双透镜){ this.len=len; } } /** *向图形中添加新节点并返回它。 */ Node addNode(){ 节点=新节点(currentNodeIndex++); put(node,newhashmap()); 返回节点; } private int currentNodeIndex=0; /** *在节点x和y之间添加无向边。 */ 无效附加(节点x、节点y、双透镜){ 边。获取(x)。放置(y,新边数据(len)); 边。获取(y)。放置(x,新边数据(len)); } /** *假设相邻节点q是其父节点,则修饰根在p的子树。 *装饰是备忘录。没有子树被装饰两次。 */ EdgeData装饰子树(节点p、节点q){ 映射相邻=边。获取(p); EdgeData数据=相邻.get(q); if(data.h==null){ 数据h=0.0; 对于(Map.Entry x:nextant.entrySet()){ 如果(x.getKey()!=q){ double hNew=x.getValue().len+decorateSubtree(x.getKey())
M(X) = 0 IF X DOES NOT EXIST
EXIST(X) = 1 IF X EXISTS, 0 OTHERWISE

M = MAX(EXIST(LEFT) + M(LEFT), EXIST(RIGHT) + M(RIGHT))
IF SUM(EXIST(..)) = 0 THEN R = 0
IF SUM(EXIST(..)) = 1 THEN R = X.M + 1 WHERE EXIST(X) = 1

R = SUM(MAX2(x,y: x.M >= y.M >= ..)) + EXIST(x) + EXIST(y)

R: the node max distance through.
M: the node max depth.
R(NULL) = 0
R(THIS) = R OF THE CURRENT NODE

S = MAX(R(THIS), S(CHILD1), .. S(CHILDX))
TIME = N + N + N = 3N

TIME ~ O(N)