此处的树表示具有 n 节点和 n-1 边缘的非循环无向图。对于树中的每个边,计算其两侧的节点数。如果在移除边缘时,您会得到两棵树 a 和 b 的节点数,那么我想找到那些值 a 和 b 表示树中的所有边(理想情况下为O(n)时间)。
直觉上我觉得从所有“叶子”节点开始的多源BFS会产生答案,但我无法将其翻译成代码。
要获得额外的功劳,请提供适用于任何常规图表的算法。
答案 0 :(得分:2)
从任何节点运行深度优先搜索(或广泛优先搜索,如果您更喜欢)。 该节点将被称为根节点,并且所有边将仅在从根节点的方向上遍历。
对于每个节点,我们计算其根节子树中的节点数。 首次访问节点时,我们将此数字设置为1。 当完全访问子节点的子树时,我们将其子树的大小添加到父节点。
在此之后,我们知道每条边的一侧的节点数。 另一边的数字只是总数减去我们找到的数字。
(您的问题的额外信用版本涉及在图表中找到桥梁作为一个非平凡的部分,因此如果您真的感兴趣,则应该被视为单独的问题。)
答案 1 :(得分:1)
考虑以下树:
1
/ \
2 3
/ \ | \
5 6 7 8
如果我们切断节点1和2之间的边缘,那么树肯定会分成两个树,因为根据树属性,两个节点之间只有一个唯一的边:
1
\
3
| \
7 8
和
2
/ \
5 6
因此,现在a
是以1
为根的节点数,b
是以2
为根的节点数。
> Run one DFS considering any node as root.
> During DFS, for each node x, calculate nodes[x] and parent[x] where
nodes [x] = k means number of nodes of sub-tree rooted at x is k
parent[x] = y means y is parent of x.
> For any edge between node x and y where parent[x] = y:
a := nodes[root] - nodes[x]
b := nodes[x]
时间和空间复杂度O(n)
。
答案 2 :(得分:0)
请注意n=b-a+1
。因此,您无需计算边缘的两侧。这大大简化了事情。从根开始的节点上的正常递归就足够了。由于你的树是无向的,你没有真正的“根”,只需选择其中一片叶子。
你想要做的是“走下去”树,直到你到达底部。然后你从那里倒数。叶子返回1,每个递归步骤对每个边的返回值求和,然后递增1。
答案 3 :(得分:0)
这是Java代码。函数countEdges()将树的邻接列表也作为当前节点和当前节点的父节点的参数(此处,父节点表示当前节点由此DFS中的父节点引入)。
在这里edge [] []存储edge [i] [j]一侧的节点数,显然另一侧的节点数等于(总节点数-edge [i] [j ])。
int edge[][];
int countEdges(ArrayList<Integer> adj[], int cur, int par) {
// If current nodes is leaf node and is not the node provided by the calling function then return 1
if(adj[cur].size() == 1 && par != 0) return 1;
int count = 1;
// count the number of nodes recursively for each neighbor of current node.
for(int neighbor: adj[cur]) {
if(neighbor == par) continue;
count += countEdges(adj, neighbor, cur);
}
// while returning from recursion assign the result obtained in the edge[][] matrix.
return edge[par][cur] = count;
}
由于我们在DFS中仅访问每个节点一次,因此时间复杂度应为O(V)。