树中距离之和【LC834】
给定一个无向、连通的树。树中有
n
个标记为0...n-1
的节点以及n-1
条边 。给定整数
n
和数组edges
,edges[i] = [ai, bi]
表示树中的节点ai
和bi
之间有一条边。返回长度为
n
的数组answer
,其中answer[i]
是树中第i
个节点与所有其他节点之间的距离之和。
新知识又++
-
思路
-
预处理:从节点0出发,进行dfs,求出节点0到每个节点的距离;同时求出以节点iii为根的子树中的节点个数,并记录在s[i]s[i]s[i]中
-
换根dp:然后从0出发再次dfs,假设u为根,v为孩子,那么当以uuu为根转移到以vvv为根时,会发生以下变化:
- 所有在vvv的子树上的节点深度减小了1,那么总深度减少了s[v]s[v]s[v]
- 所有不在uuu的子树上的节点深度增加了1,那么总深度增加了n−s[v]n-s[v]n−s[v]
状态转移方程为
fv=fu−sv+n−sv=fu+n−2sv f_v=f_u-s_v+n-s_v=f_u+n-2s_v fv=fu−sv+n−sv=fu+n−2sv
-
-
代码
class Solution { private List<Integer>[] g; private int[] ans, size; public int[] sumOfDistancesInTree(int n, int[][] edges) { g = new ArrayList[n]; // g[x] 表示 x 的所有邻居 Arrays.setAll(g, e -> new ArrayList<>()); for (var e : edges) { int x = e[0], y = e[1]; g[x].add(y); g[y].add(x); } ans = new int[n]; size = new int[n]; dfs(0, -1, 0); // 0 没有父节点 reroot(0, -1); // 0 没有父节点 return ans; } private void dfs(int x, int fa, int depth) { ans[0] += depth; // depth 为 0 到 x 的距离 size[x] = 1; for (int y : g[x]) { // 遍历 x 的邻居 y if (y != fa) { // 避免访问父节点 dfs(y, x, depth + 1); // x 是 y 的父节点 size[x] += size[y]; // 累加 x 的儿子 y 的子树大小 } } } private void reroot(int x, int fa) { for (int y : g[x]) { // 遍历 x 的邻居 y if (y != fa) { // 避免访问父节点 ans[y] = ans[x] + g.length - 2 * size[y]; reroot(y, x); // x 是 y 的父节点 } } } } 作者:灵茶山艾府 链接:https://leetcode.cn/problems/sum-of-distances-in-tree/solutions/2345592/tu-jie-yi-zhang-tu-miao-dong-huan-gen-dp-6bgb/ 来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
- 复杂度
- 时间复杂度:O(n)\mathcal{O}(n)O(n)
- 空间复杂度:O(n)\mathcal{O}(n)O(n)
- 复杂度