Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。
前言
今天的题目为中等,最近已经是连着两天都是数据结构相关的题目了,想要做好这种题目,首先就要去掌握基础,对于树来说,最基础的就是dfs方法了。
每日一题
今天的每日一题 2049. 统计最高分的节点数目,难度为中等
-
给你一棵根节点为 0 的 二叉树 ,它总共有 n 个节点,节点编号为 0 到 n - 1 。同时给你一个下标从 0 开始的整数数组 parents 表示这棵树,其中 parents[i] 是节点 i 的父节点。由于节点 0 是根,所以 parents[0] == -1 。
-
一个子树的 大小 为这个子树内节点的数目。每个节点都有一个与之关联的 分数 。求出某个节点分数的方法是,将这个节点和与它相连的边全部 删除 ,剩余部分是若干个 非空 子树,这个节点的 分数 为所有这些子树 大小的乘积 。
-
请你返回有 最高得分 节点的 数目 。
示例 1:
输入:parents = [-1,2,0,2,0]
输出:3
解释:
- 节点 0 的分数为:3 * 1 = 3
- 节点 1 的分数为:4 = 4
- 节点 2 的分数为:1 * 1 * 2 = 2
- 节点 3 的分数为:4 = 4
- 节点 4 的分数为:4 = 4
最高得分为 4 ,有三个节点得分为 4 (分别是节点 1,3 和 4 )。
示例 2:
输入:parents = [-1,2,0]
输出:2
解释:
- 节点 0 的分数为:2 = 2
- 节点 1 的分数为:2 = 2
- 节点 2 的分数为:1 * 1 = 1
最高分数为 2 ,有两个节点分数为 2 (分别为节点 0 和 1 )。
提示:
- n == parents.length
- 2 <= n <= 105
- parents[0] == -1
- 对于 i != 0 ,有 0 <= parents[i] <= n - 1
- mparents 表示一棵二叉树。
题解
dfs
重新构建树的结构数组
题目给了我们一个包含所有父节点的数组,这个数组并不是那么好看懂 parents[i] 是节点 i 的父节点 那么对于输入 [-1,2,0,2,0] 我们根据下图做一个理解
parents[i] 是节点 i 的父节点 那么 0 这个索引对应 -1,说明它是根节点,1 对应 2,那么说明它是 2 的子节点,以此类推,我们就能够得到整个的树。那么在我们看来这样的方法是能够很容易看出一个树的全貌的,但是要怎么用代码去更好地描述这棵树,这就是我们需要考虑的。
我们可以使用一个算法,来吧这个数组的结构转变为我们比较好操作的,我们可以将得到的子节点都进行转换,用一个二维数组来表示,当前节点的子节点是谁:
const children = new Array(n).fill(0);
for (let i = 0; i < n; i++) {
children[i] = [];
}
for (let i = 0; i < n; i++) {
const ch = parents[i];
if (ch !== -1) {
children[ch].push(i);
}
}
这样是不是就能够先把题目给的数组转化为比较好理解的数组,它代表着每个位置它的子节点分别是谁。
深度优先搜索
假设我们按照题目说的去掉了某一个结点,那么剩下的部分就会是,它的右子节点,它的左子节点,和它的父节点,当然,子节点有可能是不存在的。
那么根据我们上面得到的子节点树,已经这三部分,我们可以根据深度优先搜索的递归方法,去计算每一个子树的结点值,然后返回给上一级,那么最后就可以统计最大值出现的次数。
var countHighestScoreNodes = function (parents) {
const n = parents.length;
let maxSco = 0;
let res = 0;
// ... 省去获得子数组方法
const dfs = (elm) => {
let sco = 1;
let s = n - 1;
for (const c of children[elm]) {
let val = dfs(c);
sco = sco * val;
s = s - val;
}
if (elm !== 0) {
sco = sco * s;
}
if (sco === maxSco) {
res++;
} else if (sco > maxSco) {
maxSco = sco;
res = 1;
}
return n - s;
};
dfs(0);
return res;
};