【LeetCode 每日一题】1339. 分裂二叉树的最大乘积

Problem: 1339. 分裂二叉树的最大乘积

文章目录

  • 整体思路
      • [1. 核心问题与功能](#1. 核心问题与功能)
      • [2. 算法与逻辑步骤](#2. 算法与逻辑步骤)
  • 完整代码
  • 时空复杂度
      • [1. 时间复杂度: O ( N ) O(N) O(N)](#1. 时间复杂度: O ( N ) O(N) O(N))
      • [2. 空间复杂度: O ( N ) O(N) O(N)](#2. 空间复杂度: O ( N ) O(N) O(N))

整体思路

1. 核心问题与功能

这段代码的核心目的是:在二叉树中删除一条边,将树分裂成两个子树,使得这两个子树的元素和的乘积最大

最终返回这个最大乘积对 10 9 + 7 10^9 + 7 109+7 取模后的结果。

2. 算法与逻辑步骤

该算法采用了两次深度优先搜索 (DFS) 的策略:

  • 第一次遍历 (dfs1) - 计算总和

    • 我们需要知道整棵树所有节点的数值总和(记为 TotalSum)。
    • 通过一次后序遍历(递归),计算出整棵树的和。
  • 第二次遍历 (dfs2) - 寻找最大乘积

    • 我们需要模拟删除每一条边的过程。
    • 实际上,删除某个节点与其父节点之间的边,就会形成一个以该节点为根的子树
    • 如果在该节点处断开,两个子树的和分别为:
      1. 当前子树和:SubTreeSum
      2. 剩余部分和:TotalSum - SubTreeSum
    • 我们在第二次 DFS 过程中,再次计算每个节点的子树和。每计算出一个子树和,就立即计算当前的乘积 SubTreeSum * (TotalSum - SubTreeSum),并更新全局最大值 ans
  • 数据结构与细节

    • ans 使用 long 类型,因为两个子树和的乘积很容易超过 int 的范围。
    • MOD 用于最后的取模操作。

完整代码

java 复制代码
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    // 定义模数常量 10^9 + 7
    private static final int MOD = 1_000_000_007;
    // 使用 long 类型存储最大乘积,防止中间计算过程溢出
    private long ans = 0;
    
    // 第一次 DFS:主要目的是计算整棵树的总和
    // 返回值是当前子树的节点和
    private int dfs1(TreeNode node) {
        if (node == null) {
            return 0;
        }
        // 后序遍历:当前节点值 + 左子树和 + 右子树和
        return node.val + dfs1(node.left) + dfs1(node.right);
    }

    // 第二次 DFS:计算子树和,并利用传入的总和 sum 计算乘积,更新最大值
    // node: 当前节点
    // sum: 整棵树的总和(由 dfs1 计算得出,作为常量传入)
    private int dfs2(TreeNode node, int sum) {
        if (node == null) {
            return 0;
        }
        
        // 递归计算当前子树的和 (curSum)
        // 逻辑与 dfs1 相同,但在过程中增加了更新 ans 的步骤
        int curSum = node.val + dfs2(node.left, sum) + dfs2(node.right, sum);
        
        // 核心逻辑:
        // curSum 是以当前 node 为根的子树和。
        // sum - curSum 是移除该子树后,剩余部分树的和。
        // 两者相乘即为切断 node 与其父节点连接后的乘积。
        // 使用 (long) 强制转换防止乘法溢出 int 范围。
        ans = Math.max(ans, (long) curSum * (sum - curSum));
        
        // 返回当前子树和,供父节点计算使用
        return curSum;
    }

    public int maxProduct(TreeNode root) {
        // 第一步:通过 dfs1 获取整棵树的所有节点值之和
        int sum = dfs1(root);
        
        // 第二步:通过 dfs2 遍历每个子树,寻找分裂后的最大乘积
        // 注意:dfs2 的返回值(整棵树的和)在这里没用到,我们只关心过程中对 ans 的更新
        dfs2(root, sum);
        
        // 第三步:将结果对 10^9 + 7 取模并转换为 int 返回
        return (int) (ans % MOD);
    }
}

时空复杂度

1. 时间复杂度: O ( N ) O(N) O(N)

  • 计算依据
    • 代码执行了两次 DFS 遍历 (dfs1dfs2)。
    • 在每次遍历中,每个节点都被访问一次。
    • 每个节点内部的操作(加法、乘法、比较大小)都是 O ( 1 ) O(1) O(1) 的常数时间操作。
    • 总时间 = O ( N ) + O ( N ) = 2 × O ( N ) O(N) + O(N) = 2 \times O(N) O(N)+O(N)=2×O(N)。
  • 结论 : O ( N ) O(N) O(N),其中 N N N 是二叉树的节点总数。

2. 空间复杂度: O ( N ) O(N) O(N)

  • 计算依据
    • 该算法没有显式地使用额外的数组或列表来存储数据(只使用了几个变量)。
    • 主要的空间消耗来自递归调用栈
    • 递归的深度取决于二叉树的高度 H H H。
    • 最坏情况下(树退化为链表),高度 H = N H = N H=N,空间复杂度为 O ( N ) O(N) O(N)。
    • 平均/最好情况下(完全二叉树),高度 H = log ⁡ N H = \log N H=logN,空间复杂度为 O ( log ⁡ N ) O(\log N) O(logN)。
  • 结论 : O ( N ) O(N) O(N)(基于最坏情况的大 O 表示法)。

参考灵神

相关推荐
用户0203388613144 分钟前
红黑树主要功能实现
算法
专注VB编程开发20年9 分钟前
压栈顺序是反向(从右往左)的,但正因为是反向压栈,所以第一个参数反而离栈顶(ESP)最近。
java·开发语言·算法
Xの哲學9 分钟前
Linux Select 工作原理深度剖析: 从设计思想到实现细节
linux·服务器·网络·算法·边缘计算
Paul_092028 分钟前
golang编程题
开发语言·算法·golang
颜酱33 分钟前
用填充表格法-继续吃透完全背包及其变形
前端·后端·算法
夏秃然36 分钟前
打破预测与决策的孤岛:如何构建“能源垂类大模型”?
算法·ai·大模型
氷泠40 分钟前
课程表系列(LeetCode 207 & 210 & 630 & 1462)
算法·leetcode·拓扑排序·反悔贪心·三色标记法
代码or搬砖43 分钟前
JVM垃圾回收器
java·jvm·算法
老鼠只爱大米1 小时前
LeetCode算法题详解 15:三数之和
算法·leetcode·双指针·三数之和·分治法·three sum
客卿1231 小时前
C语言刷题--合并有序数组
java·c语言·算法