Medium
题目描述
给你一棵二叉树,它的根为 root 。请你删除 1 条边,使二叉树分裂成两棵子树,且它们子树和的乘积尽可能大。
由于答案可能会很大,请你将结果对 10^9 + 7 取模后返回。
注意,你需要先最大化答案再取模,而不是先取模再最大化。
示例 1:
输入:root = [1,2,3,4,5,6]
输出:110
解释:删除红色的边,得到 2 棵子树,和分别为 11 和 10 。它们的乘积是 110 (11*10)
示例 2:
输入:root = [1,null,2,3,4,null,null,5,6]
输出:90
解释:删除红色的边,得到 2 棵子树,和分别为 15 和 6 。它们的乘积是 90 (15*6)
约束条件:
- 树中节点数目在范围
[2, 5 * 10^4]内 1 <= Node.val <= 10^4
提示:
- 如果我们知道某个子树的和,答案就是
max((total_sum - subtree_sum) * subtree_sum)在每个节点处的值。
解题思路
这道题的核心思路是:当我们删除一条边时,原树会被分成两个子树,我们要最大化这两个子树和的乘积。
解题思路:
两次遍历策略:首先通过一次 DFS 计算整棵树的总和,然后再次遍历时计算每个子树的和,并更新最大乘积。
关键观察:对于任意一个节点,如果我们切断它与父节点的连接,会得到两部分:
- 以该节点为根的子树,和为
subtree_sum - 剩余部分,和为
total_sum - subtree_sum - 乘积为
(total_sum - subtree_sum) * subtree_sum
- 以该节点为根的子树,和为
算法步骤:
- 第一次 DFS:计算整棵树的总和
- 第二次 DFS:遍历每个节点,计算以该节点为根的子树和,同时更新最大乘积
优化细节:为了避免整数溢出,我们使用
long long类型进行计算,最后对结果取模。
这种方法的时间复杂度是 O(n),空间复杂度是 O(h),其中 h 是树的高度。
代码实现
class Solution {
private:
long long totalSum = 0;
long long maxProd = 0;
const int MOD = 1000000007;
public:
int maxProduct(TreeNode* root) {
// 第一次遍历:计算总和
calculateSum(root);
// 第二次遍历:寻找最大乘积
dfs(root);
return maxProd % MOD;
}
private:
long long calculateSum(TreeNode* node) {
if (!node) return 0;
totalSum += node->val;
calculateSum(node->left);
calculateSum(node->right);
return totalSum;
}
long long dfs(TreeNode* node) {
if (!node) return 0;
long long leftSum = dfs(node->left);
long long rightSum = dfs(node->right);
long long currentSum = node->val + leftSum + rightSum;
// 计算删除左子树边或右子树边的乘积
if (node->left) {
maxProd = max(maxProd, leftSum * (totalSum - leftSum));
}
if (node->right) {
maxProd = max(maxProd, rightSum * (totalSum - rightSum));
}
return currentSum;
}
};
class Solution:
def maxProduct(self, root: Optional[TreeNode]) -> int:
MOD = 10**9 + 7
# 第一次遍历:计算总和
def calculate_sum(node):
if not node:
return 0
return node.val + calculate_sum(node.left) + calculate_sum(node.right)
total_sum = calculate_sum(root)
max_product = 0
# 第二次遍历:寻找最大乘积
def dfs(node):
nonlocal max_product
if not node:
return 0
left_sum = dfs(node.left)
right_sum = dfs(node.right)
current_sum = node.val + left_sum + right_sum
# 计算删除左子树边或右子树边的乘积
if node.left:
max_product = max(max_product, left_sum * (total_sum - left_sum))
if node.right:
max_product = max(max_product, right_sum * (total_sum - right_sum))
return current_sum
dfs(root)
return max_product % MOD
public class Solution {
private long totalSum = 0;
private long maxProduct = 0;
private const int MOD = 1000000007;
public int MaxProduct(TreeNode root) {
// 第一次遍历:计算总和
CalculateSum(root);
// 第二次遍历:寻找最大乘积
DFS(root);
return (int)(maxProduct % MOD);
}
private long CalculateSum(TreeNode node) {
if (node == null) return 0;
totalSum += node.val;
CalculateSum(node.left);
CalculateSum(node.right);
return totalSum;
}
private long DFS(TreeNode node) {
if (node == null) return 0;
long leftSum = DFS(node.left);
long rightSum = DFS(node.right);
long currentSum = node.val + leftSum + rightSum;
// 计算删除左子树边或右子树边的乘积
if (node.left != null) {
maxProduct = Math.Max(maxProduct, leftSum * (totalSum - leftSum));
}
if (node.right != null) {
maxProduct = Math.Max(maxProduct, rightSum * (totalSum - rightSum));
}
return currentSum;
}
}
var maxProduct = function(root) {
const MOD = 1000000007;
let totalSum = 0;
let maxProd = 0;
// 第一次遍历:计算总和
function calculateSum(node) {
if (!node) return 0;
totalSum += node.val;
calculateSum(node.left);
calculateSum(node.right);
return totalSum;
}
// 第二次遍历:寻找最大乘积
function dfs(node) {
if (!node) return 0;
const leftSum = dfs(node.left);
const rightSum = dfs(node.right);
const currentSum = node.val + leftSum + rightSum;
// 计算删除左子树边或右子树边的乘积
if (node.left) {
maxProd = Math.max(maxProd, leftSum * (totalSum - leftSum));
}
if (node.right) {
maxProd = Math.max(maxProd, rightSum * (totalSum - rightSum));
}
return currentSum;
}
calculateSum(root);
dfs(root);
return maxProd % MOD;
};
复杂度分析
| 复杂度类型 | 复杂度 | 说明 |
|---|---|---|
| 时间复杂度 | O(n) | 需要遍历树两次,每次都是 O(n) |
| 空间复杂度 | O(h) | 递归调用栈的深度,h 为树的高度,最坏情况下为 O(n) |
相关题目
- . Count Nodes With the Highest Score (Medium)