收藏 | 从底层原理到实战:小白也能掌握的AI智能体(Agent)开发指南
2026/7/27 23:15:28
给定一个有n个节点的无向树,节点编号从0到n-1。给你一个长度为n-1的二维数组edges,其中edges[i] = [ai, bi]表示树中节点ai和bi之间存在一条边。
返回一个长度为n的数组answer,其中answer[i]是树中所有其他节点到节点i的距离之和。
示例:
输入:n=6,edges=[[0,1],[0,2],[2,3],[2,4],[2,5]]输出:[8,12,6,10,10,10]两次DFS(树形DP):
核心:
第一次DFS(后序遍历):
subtreeSize[node]:以node为根的子树中节点数量dp[node]:子树中所有节点到node的距离之和第二次DFS(前序遍历):
u移动到子节点v时:v的子树中的subtreeSize[v]个节点距离减少1n - subtreeSize[v]个节点距离增加1answer[v] = answer[u] - subtreeSize[v] + (n - subtreeSize[v])answer[v] = answer[u] + n - 2 * subtreeSize[v]importjava.util.*;classSolution{privateList<List<Integer>>graph;// 邻接表表示树privateint[]subtreeSize;// subtreeSize[i] 表示以i为根的子树节点数privateint[]dp;// dp[i] 表示子树中所有节点到i的距离之和privateint[]answer;// 最终答案privateintn;// 节点总数/** * 计算树中每个节点到其他所有节点的距离之和 * 使用两次DFS的换根DP * * @param n 节点数量 * @param edges 边数组 * @return 每个节点的距离和数组 */publicint[]sumOfDistancesInTree(intn,int[][]edges){this.n=n;this.graph=newArrayList<>();this.subtreeSize=newint[n];this.dp=newint[n];this.answer=newint[n];// 初始化邻接表for(inti=0;i<n;i++){graph.add(newArrayList<>());}// 构建无向图for(int[]edge:edges){graph.get(edge[0]).add(edge[1]);graph.get(edge[1]).add(edge[0]);}// 第一次DFS:以0为根,计算子树大小和dp值dfs1(0,-1);// 第二次DFS:换根计算所有节点的答案dfs2(0,-1);returnanswer;}/** * 第一次DFS(后序遍历): * 计算每个节点的子树大小和子树内距离和 * * @param node 当前节点 * @param parent 父节点(避免回溯) */privatevoiddfs1(intnode,intparent){subtreeSize[node]=1;// 至少包含自己dp[node]=0;// 初始化距离和为0// 遍历所有子节点for(intchild:graph.get(node)){if(child==parent){continue;// 跳过父节点}dfs1(child,node);// 递归处理子树// 累加子树信息subtreeSize[node]+=subtreeSize[child];// 子树中每个节点到当前节点的距离 = 到子节点的距离 + 1// 总距离 = dp[child] + subtreeSize[child]dp[node]+=dp[child]+subtreeSize[child];}}/** * 第二次DFS(前序遍历): * * @param node 当前节点 * @param parent 父节点 */privatevoiddfs2(intnode,intparent){// 当前节点的答案就是dp[node](第一次DFS已计算根节点答案)answer[node]=dp[node];// 遍历所有子节点,进行换根for(intchild:graph.get(node)){if(child==parent){continue;// 跳过父节点}// 换根:从node移动到child// 保存原始值intoriginalSubtreeSizeNode=subtreeSize[node];intoriginalSubtreeSizeChild=subtreeSize[child];intoriginalDpNode=dp[node];intoriginalDpChild=dp[child];// 更新子树大小(换根后)subtreeSize[node]=n-subtreeSize[child];subtreeSize[child]=n;// 更新dp值:answer[child] = answer[node] + n - 2 * subtreeSize[child](换根前的值)dp[child]=dp[node]-subtreeSize[child]+(n-subtreeSize[child]);// 递归处理子树dfs2(child,node);}}}输入:n = 6, edges = [[0,1],[0,2],[2,3],[2,4],[2,5]]
dfs1(0, -1):
dfs1(1, 0)subtreeSize[1] = 1,answer[1] = 0dfs1(2, 0)dfs1(3, 2)→subtreeSize[3] = 1,answer[3] = 0dfs1(4, 2)→subtreeSize[4] = 1,answer[4] = 0dfs1(5, 2)→subtreeSize[5] = 1,answer[5] = 0subtreeSize[2] = 1 + 1 + 1 + 1 = 4answer[2] = (0+1) + (0+1) + (0+1) = 3subtreeSize[0] = 1 + 1 + 4 = 6answer[0] = (0+1) + (3+4) = 8第一次DFS后:
subtreeSize = [6, 1, 4, 1, 1, 1]answer = [8, 0, 3, 0, 0, 0]dfs2(0, -1):
answer[1] = answer[0] + 6 - 2 * subtreeSize[1] = 8 + 6 - 2 = 12dfs2(1, 0)(1没有其他子节点)answer[2] = answer[0] + 6 - 2 * subtreeSize[2] = 8 + 6 - 8 = 6dfs2(2, 0):answer[3] = 6 + 6 - 2 = 10answer[4] = 6 + 6 - 2 = 10answer[5] = 6 + 6 - 2 = 10answer = [8, 12, 6, 10, 10, 10]
importjava.util.Arrays;publicclassMain{publicstaticvoidmain(String[]args){Solutionsolution=newSolution();// 测试用例1:标准示例intn1=6;int[][]edges1={{0,1},{0,2},{2,3},{2,4},{2,5}};int[]result1=solution.sumOfDistancesInTree(n1,edges1);System.out.println("Test 1: "+Arrays.toString(result1));// [8, 12, 6, 10, 10, 10]// 测试用例2:两个节点intn2=2;int[][]edges2={{0,1}};int[]result2=solution.sumOfDistancesInTree(n2,edges2);System.out.println("Test 2: "+Arrays.toString(result2));// [1, 1]// 测试用例3:链状树intn3=4;int[][]edges3={{0,1},{1,2},{2,3}};int[]result3=solution.sumOfDistancesInTree(n3,edges3);System.out.println("Test 3: "+Arrays.toString(result3));// [6, 4, 4, 6]// 测试用例4:星形树intn4=5;int[][]edges4={{0,1},{0,2},{0,3},{0,4}};int[]result4=solution.sumOfDistancesInTree(n4,edges4);System.out.println("Test 4: "+Arrays.toString(result4));// [4, 6, 6, 6, 6]// 测试用例5:单节点intn5=1;int[][]edges5={};int[]result5=solution.sumOfDistancesInTree(n5,edges5);System.out.println("Test 5: "+Arrays.toString(result5));// [0]// 测试用例6:三个节点线性intn6=3;int[][]edges6={{0,1},{1,2}};int[]result6=solution.sumOfDistancesInTree(n6,edges6);System.out.println("Test 6: "+Arrays.toString(result6));// [3, 2, 3]}}换根DP:
换根公式:
subtreeSize[v]个节点距离-1n - subtreeSize[v]个节点距离+1-subtreeSize[v] + (n - subtreeSize[v]) = n - 2*subtreeSize[v]树:
两次遍历: