leetcode
leetcode 2201 ~ 2250
统计可能的树根数目

统计可能的树根数目

难度:

标签:

题目描述

Alice has an undirected tree with n nodes labeled from 0 to n - 1. The tree is represented as a 2D integer array edges of length n - 1 where edges[i] = [ai, bi] indicates that there is an edge between nodes ai and bi in the tree.

Alice wants Bob to find the root of the tree. She allows Bob to make several guesses about her tree. In one guess, he does the following:

  • Chooses two distinct integers u and v such that there exists an edge [u, v] in the tree.
  • He tells Alice that u is the parent of v in the tree.

Bob's guesses are represented by a 2D integer array guesses where guesses[j] = [uj, vj] indicates Bob guessed uj to be the parent of vj.

Alice being lazy, does not reply to each of Bob's guesses, but just says that at least k of his guesses are true.

Given the 2D integer arrays edges, guesses and the integer k, return the number of possible nodes that can be the root of Alice's tree. If there is no such tree, return 0.

 

Example 1:

Input: edges = [[0,1],[1,2],[1,3],[4,2]], guesses = [[1,3],[0,1],[1,0],[2,4]], k = 3
Output: 3
Explanation: 
Root = 0, correct guesses = [1,3], [0,1], [2,4]
Root = 1, correct guesses = [1,3], [1,0], [2,4]
Root = 2, correct guesses = [1,3], [1,0], [2,4]
Root = 3, correct guesses = [1,0], [2,4]
Root = 4, correct guesses = [1,3], [1,0]
Considering 0, 1, or 2 as root node leads to 3 correct guesses.

Example 2:

Input: edges = [[0,1],[1,2],[2,3],[3,4]], guesses = [[1,0],[3,4],[2,1],[3,2]], k = 1
Output: 5
Explanation: 
Root = 0, correct guesses = [3,4]
Root = 1, correct guesses = [1,0], [3,4]
Root = 2, correct guesses = [1,0], [2,1], [3,4]
Root = 3, correct guesses = [1,0], [2,1], [3,2], [3,4]
Root = 4, correct guesses = [1,0], [2,1], [3,2]
Considering any node as root will give at least 1 correct guess. 

 

Constraints:

  • edges.length == n - 1
  • 2 <= n <= 105
  • 1 <= guesses.length <= 105
  • 0 <= ai, bi, uj, vj <= n - 1
  • ai != bi
  • uj != vj
  • edges represents a valid tree.
  • guesses[j] is an edge of the tree.
  • guesses is unique.
  • 0 <= k <= guesses.length

代码结果

运行时间: 251 ms, 内存: 67.9 MB


/*
 * 思路:
 * 1. 构建树的邻接表表示。
 * 2. 使用DFS遍历树,找出每个节点作为根时的父子关系。
 * 3. 使用Java Stream API计算每个节点作为根时正确的猜测数量。
 * 4. 如果正确的猜测数量不小于k,则该节点可能成为树的根。
 */
import java.util.*;
import java.util.stream.*;

public class Solution {
    public int rootCount(int[][] edges, int[][] guesses, int k) {
        int n = edges.length + 1;
        List<Integer>[] tree = new ArrayList[n];
        for (int i = 0; i < n; i++) {
            tree[i] = new ArrayList<>();
        }
        for (int[] edge : edges) {
            tree[edge[0]].add(edge[1]);
            tree[edge[1]].add(edge[0]);
        }
        int[] parent = new int[n];
        Arrays.fill(parent, -1);
        dfs(tree, parent, 0, -1);
        return (int) IntStream.range(0, n)
            .filter(i -> countCorrectGuesses(parent, guesses, i) >= k)
            .count();
    }

    private void dfs(List<Integer>[] tree, int[] parent, int node, int par) {
        parent[node] = par;
        for (int neighbor : tree[node]) {
            if (neighbor != par) {
                dfs(tree, parent, neighbor, node);
            }
        }
    }

    private long countCorrectGuesses(int[] parent, int[][] guesses, int root) {
        return Arrays.stream(guesses)
            .filter(guess -> parent[guess[1]] == guess[0])
            .count();
    }
}

解释

方法:

此题解采用了基于DFS遍历和动态计数的方法。首先,它通过DFS从一个固定的节点(节点0)出发,为树中的每个节点建立其父节点的映射关系。然后,使用这个映射关系来判断Bob的每次猜测是否正确。对于每个猜测,如果猜对了(即当前的父子关系符合猜测),对应的节点的正确猜测数增加;如果猜错,则对应节点的错误猜测数增加,并将总的错误数加1。接着,再次使用DFS来从根节点开始传播这些猜测的统计结果到所有节点,以此来计算每个节点作为树根时,正确猜测的总数。最后,根据每个节点的猜测统计结果,计算出能成为树根的节点数量,即那些符合条件(至少k个正确猜测)的节点。

时间复杂度:

O(n + m)

空间复杂度:

O(n)

代码细节讲解

🦆
题解中提到使用DFS建立每个节点的父节点映射,为什么选择DFS而不是BFS?对于树结构遍历有何影响?
在树结构中,DFS和BFS都是有效的遍历方式,它们都可以用来建立父节点映射关系。选择DFS而不是BFS的原因可能是出于简化实现的考虑,因为DFS通常使用递归或者栈实现,代码较为简洁。在树这种没有环的图结构中,DFS能够确保每个节点访问一次,且在访问子节点前就完成了父节点到当前节点的映射,这样的顺序对于建立父节点映射是十分方便的。而BFS虽然也可以完成相同的任务,但在实现上需要使用队列,并且在每一层的节点都被同时处理,这在某些情况下可能会略显复杂。因此,在没有特别需求的情况下,选择DFS是一种简便的策略。
🦆
在处理Bob的猜测时,为什么错误猜测会使得对应节点的统计值减一?这种处理方式对结果有何影响?
在处理猜测时,如果猜测错误(即猜测的父子关系不符合实际的父子关系),对应节点的统计值减一是因为这种猜测对于该节点成为根节点是不利的。错误猜测反映了当前节点与其子节点之间关系的误解,如果一个节点有较多错误猜测,表明它作为根节点时,构建的树与实际的父子关系较为不符,因此这种处理可以帮助减少这些节点作为候选根节点的可能性。这种处理方式确保了只有当节点与其子节点的关系较为准确时,该节点作为根的可能性才会增加,从而帮助筛选出更可能的根节点候选。
🦆
题解中再次DFS遍历时,累计每个节点的猜测统计信息是如何实现的?具体的传播逻辑是怎样的?
在题解中,第二次DFS遍历用于从根节点开始,将每个节点的猜测统计信息传播给其子节点。具体来说,每个节点会将自己的猜测统计值(包括正确和错误的猜测数)累加到其所有直接子节点的统计值中。这种传播逻辑确保了父节点的猜测结果会影响到所有的子节点,使得每个节点的统计值在遍历结束时反映了从根到该节点路径上所有猜测的综合结果。通过这种方式,可以计算出每个节点作为树根时的总猜测正确数,进一步用于判断哪些节点满足成为树根的条件。

相关问题