持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第19天,点击查看活动详情
给你一个二叉树的根结点 root ,请返回出现次数最多的子树元素和。如果有多个元素出现的次数相同,返回所有出现次数最多的子树元素和(不限顺序)。
一个结点的 「子树元素和」 定义为以该结点为根的二叉树上所有结点的元素之和(包括结点本身)。
示例 1:
输入: root = [5,2,-3]
输出: [2,-3,4]
示例 2:
输入: root = [5,2,-5]
输出: [2]
深度优先搜索
我们可以从根结点出发,深度优先搜索这棵二叉树。对于每棵子树,其子树元素和等于子树根结点的元素值,加上左子树的元素和,以及右子树的元素和。
用哈希表统计每棵子树的元素和的出现次数,计算出现次数的最大值 ,最后将出现次数等于 的所有元素和返回。
var findFrequentTreeSum = function(root) {
const cnt = new Map();
let maxCnt = 0;
const dfs = (node) => {
if (!node) {
return 0;
}
const sum = node.val + dfs(node.left) + dfs(node.right);
cnt.set(sum, (cnt.get(sum) || 0) + 1);
maxCnt = Math.max(maxCnt, cnt.get(sum));
return sum;
}
dfs(root);
const list = [];
for (const [s, c] of cnt.entries()) {
if (c === maxCnt) {
list.push(s);
}
}
const ans = new Array(list.length);
for (let i = 0; i < list.length; ++i) {
ans[i] = list[i];
}
return ans;
};
复杂度分析
- 时间复杂度:O(n),其中 n 是二叉树的结点个数。深度优先搜索的时间为 O(n)。
- 空间复杂度:O(n)。哈希表和递归的栈空间均为 O(n)。
算法步骤
-
准备几个全局变量:
- mxOcc: 记录出现元素最多的次数。即“maximum occurrence"。
- mp:哈希表,用于维护每一个节点元素和出现的次数。
- ans:用于记录答案数组。
-
对于每一个节点,我们首先递归查询其
左子树的和,然后再递归查询其右子树的和,加上这个当前节点本身的值,那么就为该节点的元素和。- 即
cur = dfs(root.left) + dfs(root.right) + root.val。
- 即
-
即
cur = dfs(root.left) + dfs(root.right) + root.val。- 即
mp[cur] += 1
- 即
-
将该元素和出现的次数与
mxOcc做比较。- 如果mp[cur] > mxOcc。那么则说明当前元素和的个数是最多的。于是将mxOcc跟新为当前元素和的数量,即mxOcc = mp[cur],然后重新记录答案数组。ans = [cur]
- 如果
mp[cur] == mxOcc。那么则说明当前元素和的个数刚好达到了最多的出现次数,直接将其元素和加入到答案数组中。ans.append(cur)
-
返回当前元素和。所有递归完成后直接返回答案数组即可。
var findFrequentTreeSum = function(root) {
let mxOcc = 0;
const mp = new Map();
let ans = [];
const dfs = (root) => {
if(!root) return 0;
const cur = root.val + dfs(root.left) + dfs(root.right);
mp.set(cur, (mp.get(cur) || 0) + 1);
if(mp.get(cur) > mxOcc) {
mxOcc = mp.get(cur);
ans = [cur];
} else if(mp.get(cur) == mxOcc) {
ans.push(cur);
}
return cur;
}
dfs(root);
return ans;
};