Algorithm 树的中心点?
假设我们有一棵有根的有序树。对于每个节点,我们都有一个子节点的链接列表。P[i]是节点i到所有其他节点的距离之和。有没有一种算法,我们可以找到树中P[i]最小的一个节点(可能是几个相等的P[i]),在最坏的情况下花费O(n)个时间?Algorithm 树的中心点?,algorithm,graph,tree,Algorithm,Graph,Tree,假设我们有一棵有根的有序树。对于每个节点,我们都有一个子节点的链接列表。P[i]是节点i到所有其他节点的距离之和。有没有一种算法,我们可以找到树中P[i]最小的一个节点(可能是几个相等的P[i]),在最坏的情况下花费O(n)个时间?这里是一些工作的O(n)代码。在本例中,我使用图{0:[1,2,3],1:[4,5],2:[6]} 我把它编出来是为了好玩。对于下图,它发现中心是节点0,其P[i]值为9。来自i->P[i]的映射是{0:9,1:10,2:12,3:14,4:15,5:15,6:17}
这里是一些工作的O(n)代码。在本例中,我使用图{0:[1,2,3],1:[4,5],2:[6]}
我把它编出来是为了好玩。对于下图,它发现中心是节点0,其P[i]值为9。来自i->P[i]的映射是{0:9,1:10,2:12,3:14,4:15,5:15,6:17}nodes={0:[1,2,3],1:[4,5],2:[6]}
PB={}
NB={}
def below(i):
if i not in nodes:
PB[i]=0
NB[i]=1
return 0,1
tot_nodes_below=0
tot_paths_below=0
for node in nodes[i]:
paths_below,nodes_below=below(node)
tot_nodes_below+=nodes_below
tot_paths_below+=paths_below
tot_paths_below+=tot_nodes_below
tot_nodes_below+=1
PB[i]=tot_paths_below
NB[i]=tot_nodes_below
return tot_paths_below,tot_nodes_below
P={0:below(0)[0]}
def fill_P(i):
for node in nodes[i]:
P[node]=P[i]+7-2*NB[node] #7 is the number of nodes
if node in nodes:
fill_P(node)
fill_P(0)
_min=min(P.values())
answers=[k for k in P if P[k]==_min]
print answers
"[0]"
说明:
这个代码是O(N)(我想是吧?)
基本上nodes=dict,它显示每个父节点连接到其子节点
让T[i]成为“树i”。我将其定义为从节点I开始的子树。(例如,T[2]=2:6,而T[0]是整棵树,T[1]将是1:[4,5]。)
现在NB[i]是T[i]中的节点数
NB={0:7,1:3,2:2,3:1,4:1,5:1,6:1}
PB[i]是T[i]内节点到i的距离之和。(所以PB[i]基本上是P[i],除了我们只看T[i]而不是T[0]
PB={0:9,1:2,2:1,3:0,4:0,5:0,6:0}
请参见PB[0]=9,因为在T[0]中有9条路径指向0。PB[6]=0表示NB[0]=1等
因此,为了实际构建PB和NB,我们需要递归O(N)函数“below(i)”
下面的(i)从根节点开始,沿着每个子树T[i]向下。对于每个子树,它计算出NB[i]和PB[i]。注意,递归的基本情况很简单,如果节点没有子节点,则PB[i]=0和NB[i]=1
为了计算具有子节点的节点的PB[i]和NB[i],我们使用递归公式。
让节点i有子节点x1..xj,然后NB[i]=1+和(NB[x])
有一个类似的递归公式来计算PB[i]
PB[i]=SUM(PB[x])+NB[i]我们添加NB[i]的原因是,下面的每个节点必须移动额外的距离1才能从子树T[x]到达节点i
一旦我们下面(i)的函数填充了NB和PB,我们就可以使用这两个结果来找出p
fill_p(i)使用NB和PB来实现这一点
其思想是,如果节点i和j彼此靠近,则p[i]将接近p[j]的值
事实上,让我们看看是否可以用NB[1],PB[1]和p[0]算出p[1]
结果是p[1]=p[0]+7-2*NB[1]
(我们甚至不需要使用PB的结果,但是我们需要PB来获得初始P[0]值)。
要了解此公式为真的原因,请思考为什么P[1]不等于P[0]。这有助于获得树的图片。让我们通过删除节点1将树拆分为两部分。现在,这将给出树的左侧(不包括节点0)和右侧(包括节点0)。注意,树的左侧是T[1],我们得到了结果NB[1]。P[1]与P[0]相同,只是T[1]行程距离小于1的节点的所有路径不同。来自不在T[1]中的节点的所有路径将进一步移动1(通过节点0到达节点1)。路径的数目分别是简单的NB[1]和7-NB[1]。我们有P[1]=P[0]+(7-NB[1])-NB[1],它给出了我们需要的公式
现在我们得到了p[0]和p[1]的正确p值。我们可以计算节点1或节点0的任何子节点的值。fill_P只是通过应用这个公式的每个子节点,我们只剩下结果P。我们只是通过P迭代找到最小值,这就是我们的结果
希望这是有意义的,干杯。我认为你可以在时间O(n)内计算所有节点的p[I]——当然包括内部节点,它们往往是具有小p[I]的节点。在这之后,找到最小的P[i]是一个额外的O(n),因此总和是O(n) 考虑在节点之间发送消息。从任意节点开始,该节点可以是根节点。向该节点的每个邻居发送关于节点和总长度的信息请求。从每个邻居接收消息,给出子树中以该邻居为根的节点数,以及该子树中所有节点到该邻居的总距离 由此算出根的p[i],并向每个邻居发送一条消息,给出所有树中的节点数和到根的总距离,但在该邻居处生根的子树除外 在每个节点(不是发起者)中,将第一条消息作为一个类似的请求传播给所有邻居,除了发送请求的一个邻居。求节点数的总和。每个距离加上与之相关的节点数和到应答中发送的邻居的距离的乘积。合计这些总数,以将响应发送回原始请求 当您从发起者处收到一条消息,给出整个树(子树除外)的距离和计数之和时,将其与您发回的消息中的信息结合起来,计算出您发回给您发送查询消息的节点的类似消息的p[i]和总数
这将在所有节点上计算p[i]。节点之间的每个链接只能看到少量恒定数量的消息。每条消息只需要少量的固定工作量(一些小计需要作为一个组的总数进行计算-一小部分)。因此,成本是O(n)。您只需要每个节点查找到距离它最远的节点的距离,而不需要求和。这些距离最短的将是树的“中心” 对于算法,您可以查看 [编辑]使用总和时可能出现不完整(或更糟)的答案:
取{R:(a),a:(b,c),c:(d)}然后得到如下的和:
R-8
a-5
b-8
c-6
d-9
它清楚地将
a
作为中心点但是,当您使用“传统方法”时,您会得到:
R-d3
a-d2
b-d3
c-R|b2
d-R|b3
它将
a
和c
作为中心点这至少显示了一种情况,即使用总和将导致不完整的答案。这