[LeetCode][Medium] Maximum Product of Splitted Binary Tree 分裂二叉树的最大乘积 | Java

2,477 阅读3分钟

问题

Maximum Product of Splitted Binary Tree Medium

Given the root of a binary tree, split the binary tree into two subtrees by removing one edge such that the product of the sums of the subtrees is maximized.

Return the maximum product of the sums of the two subtrees. Since the answer may be too large, return it modulo 10^9 + 7.

Note that you need to maximize the answer before taking the mod and not after taking it.

 

Example 1:

Input: root = [1,2,3,4,5,6]
Output: 110
Explanation: Remove the red edge and get 2 binary trees with sum 11 and 10. Their product is 110 (11*10)

Example 2:

Input: root = [1,null,2,3,4,null,null,5,6]
Output: 90
Explanation: Remove the red edge and get 2 binary trees with sum 15 and 6.Their product is 90 (15*6)

Example 3:

Input: root = [2,3,9,10,7,8,6,5,4,11,1]
Output: 1025

Example 4:

Input: root = [1,1]
Output: 1

  Constraints:

  • The number of nodes in the tree is in the range [2, 5 * 10^4].
  • 1 <= Node.val <= 10^4

解题思路

看完题目之后不难发现,本题的关键在于如何高效地计算出任意两个子树的和。分别搜索两棵子树,累加所有节点可以求出子树的和,但这样做会出现很多重复计算,显然不是最好的解法。比如题目中的Example 1, 以12作为根节点划分,或者以13作为根节点划分,下面的子节点都需要遍历求和,导致重复计算。那么如何避免呢?仔细观察可以发现,如果知道整棵树的和sumTotal以及当前子树的和sumCurr,那么剩下子树的和就是sumTotal - sumCurr,而两棵子树的乘积就是(sumTotal - sumCurr)*sumCurr。如果能分析到这里,问题已经解决了一半。

按照上面的思路,我们可以这样做:

  1. 遍历二叉树,求出所有节点的总和sumTotal
  2. 再次遍历每个节点,求出以当前节点为根节点的子树的和sumCurr
  3. 求出当前的乘积(sumTotal - sumCurr)*sumCurr,并记录下最大的乘积

这种解法可以被LeetCode接受,但是执行时间只能达到中位数左右,还可以进一步优化。

上面的解法,对于每棵子树先后执行了两次求和运算,第一次求总和,第二次求最大乘积,是否可以将第一次的求和结果保存下来,用于第二次的求解?最简单的办法,可以构造另外一棵树,保存每个节点的子树的和。那小伙伴就会问了,有没有不增加空间复杂度的办法呢?有的。每个节点val,在第一次求和之后就不再使用了,正好可以废物利用一下,将sumCurr保存到val。到这里,我们已经非常接近最优解了。

以防万一,我们检查一下将sumCurr保存到Node.val会不会溢出。由于 1 <= Node.val <= 10^4,而节点数的范围在[2, 5 * 10^4],所以最大的sumCurr5 * 10^8,在int范围内。而乘积会超过int的范围,需要用long来保存。

参考答案

class Solution {
    // sum of the total tree
    int sumTotal = 0;
    long max = 0L;
    public int maxProduct(TreeNode root) {
        int modulo = 1000000007;
        sumTotal = sum(root);
        scanMax(root);
        return (int) (max % modulo);
    }

    int sum(TreeNode node) {
        int sumLeft = 0;
        int sumRight = 0;
        if (node.left != null) {
            sumLeft = sum(node.left);
        }
        if (node.right != null) {
            sumRight = sum(node.right);
        }
        // save the sum of current subtree
        node.val = node.val + sumLeft + sumRight;
        return node.val;
    }

    void scanMax(TreeNode node) {
        // if cut current node from its parent
        // sum of the current subtree is node.val
        // sum of the other subtree is sumTotal - node.val
        // the product will be (sumTotal - node.val) * node.val
        max = Math.max(max, ((long) sumTotal - node.val) * node.val);
        if (node.left != null) {
            scanMax(node.left);
        }
        if (node.right != null) {
            scanMax(node.right);
        }
    }
}

image.png

拓展训练

来试试下面这个二叉树的题目吧!

[Leetcode][Medium] Count Good Nodes in Binary Tree 统计二叉树中好节点的数目 | Java

或者到作者的LeetCode专栏中看看,有没有其他感兴趣的问题吧!

juejin.cn/column/6997…

资料链接

  • 原题

leetcode.com/problems/ma…