📜  最小化二叉树一分为二后形成的子树之和的绝对差(1)

📅  最后修改于: 2023-12-03 15:10:36.174000             🧑  作者: Mango

最小化二叉树一分为二后形成的子树之和的绝对差

问题描述

给定具有N个结点的二叉树,要将此二叉树划分为两个集合,使两部分所有结点的子树之和的绝对差最小。返回此最小值。

示例:

输入:
    5
   / \
  2   6
 / \
1   9
输出:
    1
解释:
将树分为两个子树: [5,2] 和 [6,1,9],他们的和分别为 7 和 16,使得他们之间的差的绝对值最小为 1。
解题思路

对于一棵树,要将其分为两个集合,使得两部分所有结点的子树之和的绝对差最小,我们可以用动态规划来求解。

我们可以设dp[i][j]表示前i个结点,将其分为两个集合,其中一个集合的重量为j时,两个集合的子树之和的最小差值。对于每个结点,我们可以选择将其放入第一个集合或第二个集合。

  • 如果将结点i放入第一个集合,那么dp[i][j] = min(dp[i-1][j], dp[i-1][j-w[i]] + w[i]),其中w[i]表示结点i的子树之和。
  • 如果将结点i放入第二个集合,那么dp[i][j] = min(dp[i-1][j], dp[i-1][j+w[i]] - w[i])

最后,我们只需要在dp[N][ j]中选出使|dp[N][j]|最小的j即可。

代码实现
class Solution:
    def minimumDifference(self, root: TreeNode) -> int:
        def subtreeSum(node):
            if not node:
                return 0
            return node.val + subtreeSum(node.left) + subtreeSum(node.right)

        def dfs(node):
            if not node:
                return
            l, r = node.left, node.right
            dfs(l)
            dfs(r)
            for i in range(len(dp)):
                dp[i] += (subtreeSum(l) if l else 0) - (subtreeSum(r) if r else 0)
                if abs(dp[i]) < abs(dp[i] - 2 * sum_val):  # 更新最小值
                    nonlocal res
                    res = abs(dp[i])
                dp[i] = max(dp[i], dp[i] - 2 * sum_val)  # 避免下一次循环重复减去

        dp = [0] * 20001  # 因为子树之和最大为10000,所以默认为20000
        sum_val = subtreeSum(root)
        res = float('inf')
        dfs(root)
        return res
性能分析
  • 时间复杂度:$O(n^2)$,其中$n$为二叉树的结点数。
  • 空间复杂度:$O(n)$。

根据题目给出的数据范围,可以通过本题。