题目描述
给出一颗以1为根的树,树上节点的值只为1或者0,在最多经过两个值为1的节点的情况下,有多少条到达叶节点的路径?
输入
第一个参数为 n ,(1≤n≤100,000)
第二个参数为大小为 n-1 的点对 的集合,其中 表示结点 ui 与结点 vi之间有一条边, ,
第三个参数为大小为 n 的 0/1 序列 f , 为i-1结点的值。
返回
一个整数,表示路径数
示例1
输入:7,[(7,2),(6,1),(5,2),(1,2),(4,6),(6,3)],[0,0,1,0,1,0,0]
返回值:4
说明:样例中的四条路径分别为: (1 - 2 - 7), (1 - 2 - 5) , (1 - 6 - 3), (1 - 6 - 4)
题目分析
题目与“NC614”类似,都是给出了n-1条边,通过构建邻接表来表示树结构,然后再对树进行遍历得到想要的结果;
邻接表主要是对每个结点都创建一个存储相邻的结点的列表,在列表中会重复记录父节点,所以需要一个访问数组 visited 来标记结点是否已经被访问过,第一次遇到说明是父节点,会继续遍历它的邻接表,后面再次遇到说明是子节点的邻接点,可以跳过;
题目要求的在最多经过两个值为1的节点的情况下,可以直接将经过的结点值相加,若路径和小于等于2即满足要求;
解题思路
方法1:创建邻接表表示树信息,使用dfs遍历树
dfs的思路比较直观:
1.创建邻接表graph,根据Edge数组初始化邻接表;
2.创建访问数组 visited 来标记结点是否已经访问过;
3.dfs从根开始遍历,访问根的子节点(邻接表),在访问的结点中若没有子节点,则说明是叶子结点,同时使用一个sum统计路过的叶子结点的值和,当sum<=2时,res加1;
方法2:创建邻接表表示树信息,使用bfs遍历树
bfs遍历树,一般借助队列来实现,在这里的队列不仅要记录树结点,还需要记录经过的结点和,所以在队列中存放一个长度为2的数组array,array[0]表示树结点,array[1]表示从根节点到当前结点的路径和,判断是否是叶子结点与dfs相似,满足叶子结点和array[1]<=2,res加1。
代码实现
方法1:dfs遍历
// 邻接表 List<List<Integer>> graph = new ArrayList<>(); int res = 0; public int solve (int n, Point[] Edge, int[] f) { // write code here // 初始化邻接表 for(int i=0;i<n;i++){ graph.add(new ArrayList<>()); } for(int i=0;i<Edge.length;i++){ int u = Edge[i].x-1; int v = Edge[i].y-1; graph.get(u).add(v); graph.get(v).add(u); } // 定义访问数组 boolean[] visited = new boolean[n]; // 从根节点开始遍历 visited[0] = true; dfs(0, visited, f, f[0]); return res; } public void dfs(int i, boolean[] visited, int[] f, int sum){ // 已经超过限制,直接返回 if(sum > 2) return; // 从根节点遍历到叶子结点 List<Integer> list = graph.get(i); // 标记是否为叶子结点 boolean isLeaf = true; for(Integer child:list){ if(!visited[child]){ visited[child] = true; isLeaf = false; dfs(child, visited, f, sum+f[child]); } } if(isLeaf){ res++; } }
时间复杂度:,首先需要创建长度为n的邻接表,同时也要遍历Edge数组,在dfs的过程中,在每个结点都只有一个子节点的情况下,递归深度达到最大n,所以花费的总时间是n级别;
空间复杂度:,空间上需要2(n-1)来存邻接结点(n个结点的树有n-1条边,边上的两个结点都会在邻接表中重复出现,所以是2(n-1)),还需要深度最大为n的递归栈空间;
方法2:bfs遍历
// 邻接表 List<List<Integer>> graph = new ArrayList<>(); int res = 0; public int solve (int n, Point[] Edge, int[] f) { // write code here // 初始化邻接表 for(int i=0;i<n;i++){ graph.add(new ArrayList<>()); } for(int i=0;i<Edge.length;i++){ int u = Edge[i].x-1; int v = Edge[i].y-1; graph.get(u).add(v); graph.get(v).add(u); } // 定义访问数组 boolean[] visited = new boolean[n]; // 从根节点开始遍历 visited[0] = true; // 借助队列实现bfs Queue<int[]> queue = new LinkedList<>(); // 存储的信息是:结点+当前路径上经过的结点和 queue.offer(new int[]{0, 0}); while(!queue.isEmpty()){ // 获取结点 int[] node = queue.poll(); int i = node[0]; int sum = node[1]; // 路径和要加上遍历到的i结点的值 sum += f[i]; boolean isLeaf = true; for(Integer child:graph.get(i)){ if(!visited[child]){ visited[child] = true; // 存在子节点则不是叶子结点 isLeaf = false; // 将子节点和子节点路径和加入队列 queue.offer(new int[]{child, sum}); } } if(isLeaf && sum <= 2){ // 到了叶子结点,且路径和小于2,符合条件 res++; } } return res; }
时间复杂度:,与dfs类似需要遍历数组,bfs使用的队列,省掉了递归的栈时间,增加了遍历邻接表的时间,时间复杂度为n级别;
空间复杂度:,与dfs相似,减少了栈空间,增加了队列来存储结点值,空间复杂度级别为n。