【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 表示法)。

参考灵神

相关推荐
码农水水5 分钟前
SpringBoot配置优化:Tomcat+数据库+缓存+日志全场景教程
java·数据库·spring boot·后端·算法·tomcat·哈希算法
JMchen1236 分钟前
Android音频处理全解析:从3A算法到空间音频,打造专业级音频体验
android·经验分享·算法·kotlin·android studio·音视频
有一个好名字7 分钟前
力扣-省份数量
python·算法·leetcode
iAkuya12 分钟前
(leetcode)力扣100 55全排列
算法·leetcode·职场和发展
2301_7657031418 分钟前
动态库热加载技术
开发语言·c++·算法
2401_8414956421 分钟前
【强化学习】REINFORCE 算法
人工智能·python·算法·强化学习·reinforce·策略梯度·蒙特卡洛
棱镜Coding42 分钟前
LeetCode-Hot100 30.两两交换链表中的节点
算法·leetcode·链表
虎头金猫1 小时前
内网导航站 “出圈”!用 cpolar 解锁 Dashy 远程访问新玩法
java·c++·python·程序人生·职场和发展·php·程序员创富
2301_790300961 小时前
C++与量子计算模拟
开发语言·c++·算法
汽车仪器仪表相关领域1 小时前
经典指针+瞬态追踪:MTX-A模拟废气温度(EGT)计 改装/赛车/柴油车排气温度监测实战全解
大数据·功能测试·算法·机器学习·可用性测试