LeetCode题解:删除节点后的最高得分节点数目
题目描述
给你一棵根节点为 0 的二叉树,总共有 n 个节点,节点编号为 0 到 n - 1。同时给你一个长度为 n 的数组 parents,其中 parents[i] 表示节点 i 的父节点。由于节点 0 是根节点,故 parents[0] == -1。
我们定义节点的分数如下:
- 删除某个节点
i,即将该节点及与它相连的所有边都删除, - 剩余的节点构成若干个非空的子树,
- 该节点的分数是这些子树大小的乘积。
你的任务是:
- 找出得分最高的节点个数。
题目示例
示例 1:
输入:parents = [-1, 2, 0, 2, 0]
输出:3
解释:
- 节点 0 删除后,剩余子树大小分别是 3 和 1,分数 = 3 * 1 = 3
- 节点 1 删除后,剩余子树大小是 4,分数 = 4
- 节点 2 删除后,剩余子树大小分别是 1、1 和 2,分数 = 1 * 1 * 2 = 2
- 节点 3 删除后,剩余子树大小是 4,分数 = 4
- 节点 4 删除后,剩余子树大小是 4,分数 = 4
最高分数是 4,节点 1、3、4 得分为 4,数量为 3。
示例 2:
输入:parents = [-1, 2, 0]
输出:2
解释:
- 节点 0 删除后,剩余子树大小是 2,分数 = 2
- 节点 1 删除后,剩余子树大小是 2,分数 = 2
- 节点 2 删除后,剩余子树大小分别是 1 和 1,分数 = 1 * 1 = 1
最高分数是 2,节点 0 和 1 得分为 2,数量为 2。
解题分析
1. 理解分数的计算方式
删除节点 i 后,树被拆成了若干个子树:
- 节点
i的左右子树(如果有), - 剩余的“父树”部分,即去除该节点及其子树后剩余的其他节点组成的部分。
节点的分数是这些子树大小的乘积。
2. 题目给出的输入特点
parents数组长度为n,表示节点的父节点。- 节点编号从
0到n-1,其中parents[0] == -1表示根节点。 - 题目保证输入形成一棵有效的二叉树。
3. 需要计算的核心信息
- 每个节点的子树大小。
- 删除某节点后各子树的大小,用以计算分数。
解题方法
构建树结构
从 parents 数组中,我们能很方便地构建每个节点的子节点列表:
children = defaultdict(list)
for i in range(1, n):
children[parents[i]].append(i)
这样,children[i] 就是节点 i 的子节点列表。
计算每个节点的子树大小
使用 DFS 遍历树,计算以每个节点为根的子树大小:
def dfs(node):
size = 1
for child in children[node]:
size += dfs(child)
subtree_size[node] = size
return size
调用 dfs(0) 计算根节点的子树大小,进而计算所有节点的子树大小。
计算每个节点的分数
删除节点 i 后,树被拆分为以下几部分:
- 节点
i的所有子树,每个子树大小已知, - 剩余树的大小(即除去该节点及其子树后剩余节点数)
计算时:
- 初始化分数为 1,
- 依次乘以每个子树的大小,
- 如果节点
i不是根节点,那么剩余的“父树”部分大小为n - subtree_size[i],也乘入分数。
示例代码:
score = 1
total = n - 1 # 除去当前节点本身
for child in children[i]:
score *= subtree_size[child]
total -= subtree_size[child]
if i != 0: # 根节点没有“父树”部分
score *= total
统计最高分数及节点数量
遍历所有节点,记录最大分数并统计达到该分数的节点数量。
代码实现
from collections import defaultdict
from typing import List
class Solution:
def countHighestScoreNodes(self, parents: List[int]) -> int:
n = len(parents)
children = defaultdict(list)
for i in range(1, n):
children[parents[i]].append(i)
subtree_size = [0] * n
def dfs(node):
size = 1
for child in children[node]:
size += dfs(child)
subtree_size[node] = size
return size
dfs(0)
max_score = 0
count = 0
for i in range(n):
score = 1
total = n - 1
for child in children[i]:
score *= subtree_size[child]
total -= subtree_size[child]
if i != 0:
score *= total
if score > max_score:
max_score = score
count = 1
elif score == max_score:
count += 1
return count
复杂度分析
- 构建子节点列表的时间复杂度:
O(n)。 - DFS计算子树大小遍历所有节点一次,时间复杂度:
O(n)。 - 计算所有节点分数遍历一次,时间复杂度:
O(n)。 - 总时间复杂度为:
O(n),空间复杂度为O(n)用于存储树结构及子树大小。
示例讲解
以示例 parents = [-1, 2, 0, 2, 0] 为例:
树结构如下:
0
/ \
4 2
/ \
1 3
- 节点 0 的子树大小为 5(整棵树),
- 删除节点 0,剩余子树是节点 4(大小1)和以 2 为根的子树(大小3),分数为 3 * 1 = 3。
- 删除节点 1,剩余树大小为 4,分数为 4。
- 删除节点 2,剩余子树分别是节点 1(大小1)、节点 3(大小1)、节点 0 和 4 组成的子树(大小2),分数为 1 * 1 * 2 = 2。
- 删除节点 3,剩余树大小为 4,分数为 4。
- 删除节点 4,剩余树大小为 4,分数为 4。
最高分数为 4,有三个节点达到该分数。
总结
这道题的关键在于:
- 明确分数定义和树结构分割方式,
- 用 DFS 计算子树大小,
- 理解删除节点后,分成若干子树的大小计算方法,
- 合理利用数组和字典构造树及辅助存储。
这种基于树的分治思路非常经典,掌握这类技巧对后续解决类似树结构的问题大有裨益。
5万+

被折叠的 条评论
为什么被折叠?



