树中距离之和
难度:
标签:
题目描述
给定一个无向、连通的树。树中有 n
个标记为 0...n-1
的节点以及 n-1
条边 。
给定整数 n
和数组 edges
, edges[i] = [ai, bi]
表示树中的节点 ai
和 bi
之间有一条边。
返回长度为 n
的数组 answer
,其中 answer[i]
是树中第 i
个节点与所有其他节点之间的距离之和。
示例 1:
输入: n = 6, edges = [[0,1],[0,2],[2,3],[2,4],[2,5]] 输出: [8,12,6,10,10,10] 解释: 树如图所示。 我们可以计算出 dist(0,1) + dist(0,2) + dist(0,3) + dist(0,4) + dist(0,5) 也就是 1 + 1 + 2 + 2 + 2 = 8。 因此,answer[0] = 8,以此类推。
示例 2:

输入: n = 1, edges = [] 输出: [0]
示例 3:

输入: n = 2, edges = [[1,0]] 输出: [1,1]
提示:
1 <= n <= 3 * 104
edges.length == n - 1
edges[i].length == 2
0 <= ai, bi < n
ai != bi
- 给定的输入保证为有效的树
代码结果
运行时间: 175 ms, 内存: 40.3 MB
/*
* 思路:
* 1. 使用流构建树的邻接表表示。
* 2. 使用两次深度优先搜索(DFS)来计算结果,配合流处理数据。
*/
import java.util.*;
import java.util.stream.*;
public class Solution {
int[] answer, count;
List<Set<Integer>> tree;
int n;
public int[] sumOfDistancesInTree(int N, int[][] edges) {
n = N;
tree = IntStream.range(0, n)
.mapToObj(i -> new HashSet<Integer>())
.collect(Collectors.toList());
Arrays.stream(edges).forEach(e -> {
tree.get(e[0]).add(e[1]);
tree.get(e[1]).add(e[0]);
});
answer = new int[n];
count = new int[n];
Arrays.fill(count, 1);
dfs(0, -1);
dfs2(0, -1);
return answer;
}
public void dfs(int node, int parent) {
tree.get(node).stream()
.filter(neighbor -> neighbor != parent)
.forEach(neighbor -> {
dfs(neighbor, node);
count[node] += count[neighbor];
answer[node] += answer[neighbor] + count[neighbor];
});
}
public void dfs2(int node, int parent) {
tree.get(node).stream()
.filter(neighbor -> neighbor != parent)
.forEach(neighbor -> {
answer[neighbor] = answer[node] - count[neighbor] + n - count[neighbor];
dfs2(neighbor, node);
});
}
}
解释
方法:
该题解采用两遍DFS(深度优先搜索)的方法。首先,建立邻接表表示树。第一次DFS计算出根节点(假设为节点0)到所有其他节点的距离总和并存储在ans[0]中,同时计算每个节点为根的子树的大小存储在size数组中。第二次DFS则利用已知的根节点的答案和子树大小,通过换根DP(动态规划)的思想,推导出其他节点的答案。具体地,当从节点x到其子节点y时,ans[y] = ans[x] + n - 2 * size[y],这是基于树中每当根节点改变时,到达某个节点的距离改变的性质计算的。这种方法有效地避免了重复计算每个节点到所有其他节点的距离,大大提高了效率。
时间复杂度:
O(n)
空间复杂度:
O(n)
代码细节讲解
🦆
为什么选择节点0作为初始的根节点进行第一次DFS,是否选择其他节点作为根节点会影响最终的结果?
▷🦆
在第一次DFS过程中,`size`数组的具体作用是什么?如何理解每个节点的`子树大小`对于计算距离和的影响?
▷🦆
第二次DFS中,公式`ans[y] = ans[x] + n - 2 * size[y]`是如何得出的?可以详细解释这个公式背后的数学逻辑吗?
▷相关问题
在二叉树中分配硬币
给你一个有 n
个结点的二叉树的根结点 root
,其中树中每个结点 node
都对应有 node.val
枚硬币。整棵树上一共有 n
枚硬币。
在一次移动中,我们可以选择两个相邻的结点,然后将一枚硬币从其中一个结点移动到另一个结点。移动可以是从父结点到子结点,或者从子结点移动到父结点。
返回使每个结点上 只有 一枚硬币所需的 最少 移动次数。
示例 1:

输入:root = [3,0,0] 输出:2 解释:一枚硬币从根结点移动到左子结点,一枚硬币从根结点移动到右子结点。
示例 2:

输入:root = [0,3,0] 输出:3 解释:将两枚硬币从根结点的左子结点移动到根结点(两次移动)。然后,将一枚硬币从根结点移动到右子结点。
提示:
- 树中节点的数目为
n
1 <= n <= 100
0 <= Node.val <= n
- 所有
Node.val
的值之和是n