LeetCode 题解工作台

统计好节点的数目

现有一棵 无向 树,树中包含 n 个节点,按从 0 到 n - 1 标记。树的根节点是节点 0 。给你一个长度为 n - 1 的二维整数数组 edges ,其中 edges[i] = [a i , b i ] 表示树中节点 a i 与节点 b i 之间存在一条边。 如果一个节点的所有子节点为根的 子…

category

2

题型

code_blocks

5

代码语言

hub

3

相关题

当前训练重点

中等 · 二分·树·traversal

bolt

答案摘要

我们先根据题目给定的边 构建出树的邻接表 ,其中 表示节点 的所有邻居节点。 然后,我们设计一个函数 $\textit{dfs}(a, \textit{fa})$,表示计算以节点 为根的子树中的节点数,并累计好节点的数量。其中 表示节点 的父节点。

Interview AiBox logo

Interview AiBox 实时 AI 助手,陪你讲清 二分·树·traversal 题型思路

试试 AiBox 面试助手arrow_forward
description

题目描述

现有一棵 无向 树,树中包含 n 个节点,按从 0n - 1 标记。树的根节点是节点 0 。给你一个长度为 n - 1 的二维整数数组 edges,其中 edges[i] = [ai, bi] 表示树中节点 ai 与节点 bi 之间存在一条边。

如果一个节点的所有子节点为根的 子树 包含的节点数相同,则认为该节点是一个 好节点

返回给定树中 好节点 的数量。

子树 指的是一个节点以及它所有后代节点构成的一棵树。

 

 

示例 1:

输入:edges = [[0,1],[0,2],[1,3],[1,4],[2,5],[2,6]]

输出:7

说明:

树的所有节点都是好节点。

示例 2:

输入:edges = [[0,1],[1,2],[2,3],[3,4],[0,5],[1,6],[2,7],[3,8]]

输出:6

说明:

树中有 6 个好节点。上图中已将这些节点着色。

示例 3:

输入:edges = [[0,1],[1,2],[1,3],[1,4],[0,5],[5,6],[6,7],[7,8],[0,9],[9,10],[9,12],[10,11]]

输出:12

解释:

除了节点 9 以外其他所有节点都是好节点。

 

提示:

  • 2 <= n <= 105
  • edges.length == n - 1
  • edges[i].length == 2
  • 0 <= ai, bi < n
  • 输入确保 edges 总表示一棵有效的树。
lightbulb

解题思路

方法一:DFS

我们先根据题目给定的边 edges\textit{edges} 构建出树的邻接表 g\textit{g},其中 g[a]\textit{g}[a] 表示节点 aa 的所有邻居节点。

然后,我们设计一个函数 dfs(a,fa)\textit{dfs}(a, \textit{fa}),表示计算以节点 aa 为根的子树中的节点数,并累计好节点的数量。其中 fa\textit{fa} 表示节点 aa 的父节点。

函数 dfs(a,fa)\textit{dfs}(a, \textit{fa}) 的执行过程如下:

  1. 初始化变量 pre=1\textit{pre} = -1, cnt=1\textit{cnt} = 1, ok=1\textit{ok} = 1,分别表示节点 aa 的某个子树的节点数、节点 aa 的所有子树的节点数、以及节点 aa 是否为好节点。
  2. 遍历节点 aa 的所有邻居节点 bb,如果 bb 不等于 fa\textit{fa},则递归调用 dfs(b,a)\textit{dfs}(b, a),返回值为 cur\textit{cur},并累加到 cnt\textit{cnt} 中。如果 pre<0\textit{pre} < 0,则将 cur\textit{cur} 赋值给 pre\textit{pre};否则,如果 pre\textit{pre} 不等于 cur\textit{cur},说明节点 aa 的不同子树的节点数不同,将 ok\textit{ok} 置为 00
  3. 最后,累加 ok\textit{ok} 到答案中,并返回 cnt\textit{cnt}

在主函数中,我们调用 dfs(0,1)\textit{dfs}(0, -1),最后返回答案。

时间复杂度 O(n)O(n),空间复杂度 O(n)O(n)。其中 nn 表示节点的数量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Solution:
    def countGoodNodes(self, edges: List[List[int]]) -> int:
        def dfs(a: int, fa: int) -> int:
            pre = -1
            cnt = ok = 1
            for b in g[a]:
                if b != fa:
                    cur = dfs(b, a)
                    cnt += cur
                    if pre < 0:
                        pre = cur
                    elif pre != cur:
                        ok = 0
            nonlocal ans
            ans += ok
            return cnt

        g = defaultdict(list)
        for a, b in edges:
            g[a].append(b)
            g[b].append(a)
        ans = 0
        dfs(0, -1)
        return ans
speed

复杂度分析

指标
时间complexity is O(n) because each node is visited once during DFS. Space complexity is O(n) for the adjacency list and recursion stack in the worst case, depending on the tree depth.
空间Depends on the final approach
psychology

面试官常问的追问

外企场景
  • question_mark

    Emphasizes DFS recursion and state tracking.

  • question_mark

    Expects handling of edge cases where nodes have varying numbers of children.

  • question_mark

    Checks for optimization in counting good nodes during traversal instead of post-processing.

warning

常见陷阱

外企场景
  • error

    Failing to track parent nodes can lead to revisiting edges and incorrect counts.

  • error

    Incorrectly calculating subtree sizes can misclassify good nodes.

  • error

    Not handling leaf nodes separately may cause off-by-one errors in counting.

swap_horiz

进阶变体

外企场景
  • arrow_right_alt

    Count nodes where all subtrees differ in size instead of being equal.

  • arrow_right_alt

    Apply the good node definition to an N-ary tree instead of a binary tree.

  • arrow_right_alt

    Return a list of all good node indices rather than just the count.

help

常见问题

外企场景

统计好节点的数目题解:二分·树·traversal | LeetCode #3249 中等