题意整理
- 给定一颗有n个节点的树,每个节点权值初始为0。
- 现在有一个查询集合,集合中每一项提供两个参数r和v,每次查询将r节点及其子节点的权值增加v。
- 返回最终每个节点的权值。
方法一(DFS)
1.解题思路
- 首先建立邻接表,用于访问某个节点的所有子节点。
- 初始化查询中当前根节点的权值。
- 按照建立的邻接表,递归地遍历当前根节点下每个子节点,如果没有访问过,则加上对应权值。按照这个规则访问完所有的节点。
由于测试数据较大,运行报栈深度溢出(StackOverflowError)。可以通过自定义栈深度,防溢出。
2.代码实现
import java.util.*; /* * public class Point { * int x; * int y; * public Point(int x, int y) { * this.x = x; * this.y = y; * } * } */ public class Solution { /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * * 从 1 到 n 每个结点的权值。 * @param n int * @param Edge Point一维数组 (u, v) 集合 * @param q int * @param Query Point一维数组 Point.x 为题中 r, Point.y为题中 v * @return long一维数组 */ //节点个数 int n; //标记数组 boolean[] v; //邻接表 List<List<Integer>> graph; //权值数组 long[] w; public long[] solve (int n, Point[] Edge, int q, Point[] Query) { //初始化 this.n=n; this.v=new boolean[n]; this.graph=new ArrayList<>(); //给邻接表赋值 for(int i=0;i<n;i++){ graph.add(new ArrayList<>()); } for(Point point:Edge){ int u=point.x; int v=point.y; graph.get(u-1).add(v-1); graph.get(v-1).add(u-1); } //给权值数组赋值 this.w=new long[n]; for(int i=0;i<q;i++){ w[Query[i].x-1]+=Query[i].y; } //从根节点开始,给每一个子节点增加权值 v[0]=true; try{ Thread t=new Thread(null,()->{ dfs(0); },"自定义栈深度",1<<30); t.start(); t.join(); } catch(Exception e){ } return w; } //递归地给子节点增加权值 private void dfs(int i){ for(Integer j:graph.get(i)){ //如果子节点没被访问到,则增加对应权值 if(!v[j]){ w[j]+=w[i]; v[j]=true; dfs(j); } } } }
3.复杂度分析
- 时间复杂度:最坏情况下,需要访问所有的节点,所以时间复杂度为。
- 空间复杂度:最坏情况下,需要额外深度为n的递归栈、大小为n的标记数组以及大小为的邻接表,所以空间复杂度为。
方法二(BFS)
1.解题思路
- 首先建立邻接表,用于访问某个节点的所有子节点。
- 初始化查询中当前根节点的权值。
- 将根节点入队,遍历当前根节点的所有子节点,如果未被访问过,则增加对应权值,同时将该节点入队,继续遍历。
动图展示:
2.代码实现
import java.util.*; /* * public class Point { * int x; * int y; * public Point(int x, int y) { * this.x = x; * this.y = y; * } * } */ public class Solution { /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * * 从 1 到 n 每个结点的权值。 * @param n int * @param Edge Point一维数组 (u, v) 集合 * @param q int * @param Query Point一维数组 Point.x 为题中 r, Point.y为题中 v * @return long一维数组 */ //节点个数 int n; //标记数组 boolean[] v; //邻接表 List<List<Integer>> graph; //权值数组 long[] w; public long[] solve (int n, Point[] Edge, int q, Point[] Query) { //初始化 this.n=n; this.v=new boolean[n]; this.graph=new ArrayList<>(); //给邻接表赋值 for(int i=0;i<n;i++){ graph.add(new ArrayList<>()); } for(Point point:Edge){ int u=point.x; int v=point.y; graph.get(u-1).add(v-1); graph.get(v-1).add(u-1); } //给权值数组赋值 this.w=new long[n]; for(int i=0;i<q;i++){ w[Query[i].x-1]+=Query[i].y; } //初始化队列 Queue<Integer> queue=new LinkedList<>(); //将根节点添加到队列 v[0]=true; queue.offer(0); while(!queue.isEmpty()){ int i=queue.poll(); //遍历当前根节点所有子节点 for(Integer j:graph.get(i)){ //如果未被访问过,则增加对应权值 if(!v[j]){ w[j]+=w[i]; v[j]=true; queue.offer(j); } } } return w; } }
3.复杂度分析
- 时间复杂度:最多访问n个节点,所以时间复杂度为。
- 空间复杂度:最坏情况下,需要额外大小为n的队列、大小为n的标记数组以及大小为的邻接表,所以空间复杂度为。