分裂二叉树的最大乘积
难度:
标签:
题目描述
给你一棵二叉树,它的根为 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)
示例 3:
输入:root = [2,3,9,10,7,8,6,5,4,11,1] 输出:1025
示例 4:
输入:root = [1,1] 输出:1
提示:
- 每棵树最多有
50000
个节点,且至少有2
个节点。 - 每个节点的值在
[1, 10000]
之间。
代码结果
运行时间: 128 ms, 内存: 35.1 MB
// 题目思路:
// 1. 计算整个二叉树的总和。
// 2. 通过递归遍历每个节点,计算每个子树的和。
// 3. 每次计算出一条边的删除后,两个子树和的乘积,并更新最大乘积。
// 4. 返回最大乘积对10^9 + 7取模的结果。
import java.util.*;
import java.util.stream.*;
class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) { val = x; }
}
public class Solution {
private long totalSum = 0;
private long maxProduct = 0;
private static final int MOD = 1_000_000_007;
public int maxProduct(TreeNode root) {
// 计算总和
totalSum = treeSum(root);
// 计算最大乘积
calculateMaxProduct(root);
return (int) (maxProduct % MOD);
}
private long treeSum(TreeNode node) {
if (node == null) return 0;
return Stream.of(node.val, treeSum(node.left), treeSum(node.right)).mapToLong(Long::longValue).sum();
}
private long calculateMaxProduct(TreeNode node) {
if (node == null) return 0;
long subTreeSum = Stream.of(node.val, calculateMaxProduct(node.left), calculateMaxProduct(node.right)).mapToLong(Long::longValue).sum();
long product = subTreeSum * (totalSum - subTreeSum);
maxProduct = Math.max(maxProduct, product);
return subTreeSum;
}
}
解释
方法:
题解采用了深度优先搜索(DFS)的方法来解题。首先,通过一个DFS遍历计算出整棵树的总和,同时修改每个节点的值,使得每个节点存储的是该节点为根的子树的总和。接着,再次通过DFS遍历树,对于每个节点计算如果以该节点的子节点为分割点,分成的两棵子树的乘积,并更新最大乘积。最后返回经过模运算的最大乘积结果。
时间复杂度:
O(n)
空间复杂度:
O(n)
代码细节讲解
🦆
为什么首先需要通过DFS遍历计算出整棵树的总和,并修改每个节点的值来存储子树的总和?
▷🦆
在第二次DFS遍历中,如何确定选择哪个子节点作为分割点?特别是在处理不平衡树时,这种选择标准是如何应用的?
▷🦆
在计算最大乘积时,为什么要更新节点值中的最大乘积而不是直接计算所有可能的乘积并从中选择最大值?
▷